Attribute Handling Special Method Names
When we reference an attribute of an object with something like
someObject.name
, Python uses several special methods to get
the someAttr
attribute of the object. Since we can
say things like myObject.anAttr= 5
, and print
myObject.anAttr
, there are actually three different implicit
methods related to attribute access.
When the attribute reference occurs on the left side of an
assignment statement, we are setting the attribute. When the attribute
occurs almost anywhere else were are getting the value of the attribute.
The final operation is deleting an attribute with the
del
statement.
While most attributes are simply instance variables, we have to
make a firm distinction between an
attribute
and an
instance variable
.
-
An attribute is a name, qualified by an object. It is a
syntactic construction. Generally, the attribute name is treated as
a key to access the object's internal collection of instance
variables, __dict__
. However, we can change the
behavior of an attribute reference.
-
An instance variable is stored in the
__dict__
of an object. Generally, we access
instance variables using attribute syntax. The attribute name is
simply the key of the instance variable in the object's
__dict__
.
When we say someObj.name
, the default behavior is
effectively someObj.__dict__['name']
.
There are several ways that we can tap into Python's internal
mechanisms for getting and setting attribute values.
-
The most accessible technique is to use the
property
function to define get, set and delete
methods associated with an attribute name. The
property
function builds descriptors for you.
We'll look at this in the section called “Properties”.
-
A slightly less accessible, but more extensible and reusable
technique is to define descriptor classes
yourself. This allows you considerable flexibility. You do this by
creating a class which defines get, set and delete methods, and you
associate this descriptor class with an attribute name. We'll look
at this in the section called “Descriptors”.
-
You can tap into Python's low-level special methods for
attribute access. There are three methods which plug into the
standard algorithm. The fourth method,
__getattribute__
, allows you to change
attribute access in a fundamental way.
Warning
Changing attribute access can interfere with how people
understand the operation of your classes and objects. The default
assumption is that an attribute is an instance variable. While we can
fundamentally alter the meaning of a Python attribute, we need to be
cautious about violating the default assumptions of people reading our
software.
Attribute Access Special Methods. Fundamentally, attribute access works through a few special
method names. Python has a default approach: it checks the object for
an instance variable that has the attribute's name before using these
attribute handling methods. Because Python uses these methods when an
attribute isn't an instance variable, you can easily create infinite
recursion. This can happen if you try to get an instance variable
using a simple self.someAttr
in the
__getattr__
method, or set the value of an
instance variable with a simple self.someAttr
in the
__setattr__
method. Within
__getattr__
and
__setattr__
, you have to use the internal
__dict__
explicitly.
These are the low-level attribute access methods.
-
__getattr__
(
self
,
name
) →
value
-
Returns a value for an attibute when the name is not an
instance attribute nor is it found in any of the parent classes.
name
is the attribute name. This method
returns the attribute value or raises an
AttributeError
exception.
-
__setattr__
(
self
,
name
,
value
)
-
Assigns a value to an attribute.
name
is the attribute name,
value
is the value to assign to it.
Note that if you naively do self.name= value in this method, you
will have an infinite recursion of
__setattr__
calls. If you want to access
the internal dictionary of attributes,
__dict__
, you have to use the following:
self
.__dict
__[
name
] =
value
.
-
__delattr__
(
self
,
name
)
-
Delete the named attribute from the object.
name
is the attribute name.
-
__getattribute__
(
self
,
name
)
→ value
-
Low-level access to a named attribute. If you provide this,
it replaces the default approach of searching for an attribute and
then using __getattr__
if the named
attribute isn't an instance variable of the class. To provide the
default approach, this method must explicitly evaluate the
superclass __getattribute__
method with
super(
Class
,self).__getattribute__(
name
)
. This only works for
classes which are derived from
object
.