Python Language OOP Attributes and Properties: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
(Created page with "=Internal= * Python Object Oriented Programming =Overview= =Attributes= =Properties=")
 
Line 7: Line 7:


=Properties=
=Properties=
-------
==Attributes==
An object contains data in form of variables called '''attributes'''.
The attributes can be accessed inside the class definition using <code>self.<attribute-name></code>. Outside the class definition, they can be accessed via the variable holding the reference to the class instance: <code>my_instance.<attribute-name></code>. Unlike in other languages, all attributes are public. If an attribute was not explicitly declared inside the instance with <code>self.some_attribute</code>, even just to be assigned to <code>None</code>, an attempt to access the attribute will end up in:
<font size=-1>
AttributeError: 'MyClass' object has no attribute 'some_attribute'
</font>
Python offers two syntactical patterns to expose internal class instance state, either represented by an actual attributes, or computed values: [[#Properties|properties]] and <code>[[#Decorators|@property/@<attribute-name>.setter]]</code> decorators.
===Naming Convention===
Prepending a single underscore (_) has some support for protecting module variables and functions, as well as class attributes and methods (linters and IDE static analysis will flag protected member access). PyCharm explicitly shows them as "Protected Attributes":
:::[[File:PyCharm_Protected_Attributes.png]]
<font color=darkkhaki>
Cross-link to [[Python_Language#Leading_Underscore_Variable_Names|Python Language &#124; Leading Underscore Variable Names]]
</font>
===Private Attributes===
<font color=darkkhaki>
Prepending a double underscore (__ aka “dunder”) to an instance variable or method effectively makes the variable or method private to its class, using name mangling. [https://google.github.io/styleguide/pyguide.html#3162-naming-conventions Google Python Style Guide] discourages this use as it impacts readability and testability, and isn’t really private. It advises to use a single underscore.
</font>
===Properties===
The state associated with a computed value can be prevented from being written by omitting the corresponding setter.
Another advantage of using properties over direct attribute access is that if the definition of the attribute changes, only the code within the class definition needs to be changed, not in all the callers.
<font color=darkkhaki>
* TO PROCESS: [[PyOOP]] "Adding behavior to class data with properties", "Properties in detail"
* TO PROCESS: [[PyOOP]] "Deciding when to use properties"
</font>
Properties are '''class construct that associate getter and setter methods to an attribute'''. The attribute name can the be used to access and write state inside the class by invoking the associated "property" methods. In other words, the getter and setter methods are "properties" of the attribute with the given name.
<syntaxhighlight lang='py'>
class A:
  def __init__(self, c):
    self.internal_color = c
  def get_color(self):
    return self.internal_color
  def set_color(self, c):
    self.internal_color = c
  # 'color' is an attribute, though not explicitly declared on self, and get_color() and set_color() are properties of the attribute
  color = property(get_color, set_color)
</syntaxhighlight>
The first argument to <code>property()</code> is the getter method, and the second argument is the setter method.
Internal class instance state can be accesses and written through the attribute, though there is no actual attribute with that name initialized on <code>self</code>:
<syntaxhighlight lang='py'>
a = A('red')
assert 'red' == a.color
a.color = 'blue'
assert 'blue' == a.color
</syntaxhighlight>
====<span id='Decorators'></span>Defining Properties with Decorators====
<font color=darkkhaki>TO PROCESS: [[PyOOP]] "Decorators - another way to create properties"</font>
Another way to define [[#Properties|properties]] is with '''decorators'''. The same attribute <code>color</code>, which is not declared on <code>self</code>, can defined by two different property methods, one getter and one setter, preceded by corresponding decorators (annotations):
* <code>@property</code>: it annotates the getter method. ⚠️ The name of the method must match the name of the attribute.
* <code>@<attribute-name>.setter</code>: it annotates the setter method. ⚠️ The  <code><attribute-name></code> that is part of the annotation must match the name of the attribute and the name of the setter method.
<syntaxhighlight lang='py'>
class A:
    def __init__(self, c):
        self.internal_color = c
    @property
    def color(self):
        return self.internal_color
    # not exposing a setter prevents the attribute from being written
    @color.setter
    def color(self, c):
        self.internal_color = c
</syntaxhighlight>
The interaction with the internal state is done identically as in the case of the <code>property()</code> declaration:
<syntaxhighlight lang='py'>
a = A('red')
assert 'red' == a.color
a.color = 'blue'
assert 'blue' == a.color
</syntaxhighlight>
For more details on decorators see: {{Internal|Python_Decorators#Overview|Python Decorators}}
===Mangling Attribute Names for Privacy===
<font color=darkkhaki>TODO</font>

Revision as of 03:42, 23 August 2022

Internal

Overview

Attributes

Properties


Attributes

An object contains data in form of variables called attributes.

The attributes can be accessed inside the class definition using self.<attribute-name>. Outside the class definition, they can be accessed via the variable holding the reference to the class instance: my_instance.<attribute-name>. Unlike in other languages, all attributes are public. If an attribute was not explicitly declared inside the instance with self.some_attribute, even just to be assigned to None, an attempt to access the attribute will end up in:

AttributeError: 'MyClass' object has no attribute 'some_attribute'

Python offers two syntactical patterns to expose internal class instance state, either represented by an actual attributes, or computed values: properties and @property/@<attribute-name>.setter decorators.

Naming Convention

Prepending a single underscore (_) has some support for protecting module variables and functions, as well as class attributes and methods (linters and IDE static analysis will flag protected member access). PyCharm explicitly shows them as "Protected Attributes":

PyCharm Protected Attributes.png

Cross-link to Python Language | Leading Underscore Variable Names

Private Attributes

Prepending a double underscore (__ aka “dunder”) to an instance variable or method effectively makes the variable or method private to its class, using name mangling. Google Python Style Guide discourages this use as it impacts readability and testability, and isn’t really private. It advises to use a single underscore.

Properties

The state associated with a computed value can be prevented from being written by omitting the corresponding setter.

Another advantage of using properties over direct attribute access is that if the definition of the attribute changes, only the code within the class definition needs to be changed, not in all the callers.

  • TO PROCESS: PyOOP "Adding behavior to class data with properties", "Properties in detail"
  • TO PROCESS: PyOOP "Deciding when to use properties"

Properties are class construct that associate getter and setter methods to an attribute. The attribute name can the be used to access and write state inside the class by invoking the associated "property" methods. In other words, the getter and setter methods are "properties" of the attribute with the given name.

class A:
  def __init__(self, c):
    self.internal_color = c

  def get_color(self):
    return self.internal_color

  def set_color(self, c):
    self.internal_color = c

  # 'color' is an attribute, though not explicitly declared on self, and get_color() and set_color() are properties of the attribute
  color = property(get_color, set_color)

The first argument to property() is the getter method, and the second argument is the setter method.

Internal class instance state can be accesses and written through the attribute, though there is no actual attribute with that name initialized on self:

a = A('red')
assert 'red' == a.color
a.color = 'blue'
assert 'blue' == a.color

Defining Properties with Decorators

TO PROCESS: PyOOP "Decorators - another way to create properties"

Another way to define properties is with decorators. The same attribute color, which is not declared on self, can defined by two different property methods, one getter and one setter, preceded by corresponding decorators (annotations):

  • @property: it annotates the getter method. ⚠️ The name of the method must match the name of the attribute.
  • @<attribute-name>.setter: it annotates the setter method. ⚠️ The <attribute-name> that is part of the annotation must match the name of the attribute and the name of the setter method.
class A:
    def __init__(self, c):
        self.internal_color = c

    @property
    def color(self):
        return self.internal_color

    # not exposing a setter prevents the attribute from being written
    @color.setter
    def color(self, c):
        self.internal_color = c

The interaction with the internal state is done identically as in the case of the property() declaration:

a = A('red')
assert 'red' == a.color
a.color = 'blue'
assert 'blue' == a.color

For more details on decorators see:

Python Decorators

Mangling Attribute Names for Privacy

TODO