Credit: Luther Blissett
You want client code to use normal attribute-access syntax for using, binding, or deleting instance attributes, but you want the semantics of these actions to be determined by method calls (e.g., to compute an attribute’s value on the fly).
With Python 2.2 new-style classes, the new built-in
property
function lets you do this directly:
class Rectangle(object): def _ _init_ _(self, width, height): self.width = width self.height = height def getArea(self): return self.width * self.height def setArea(self, value): raise AttributeError, "Can't set 'area' attribute" area = property(getArea, setArea)
With classic classes, you must implement properties yourself with the
special methods _ _getattr_ _
and _ _setattr_ _
:
class Rectangle: def _ _init_ _(self, width, height): self.width = width self.height = height def getArea(self): return self.width * self.height def setArea(self, value): raise AttributeError, "Can't set 'area' attribute" def _ _getattr_ _(self, name): if name=='area': return self.getArea( ) raise AttributeError, name def _ _setattr_ _(self, name, value): if name=='area': return self.setArea(value) self._ _dict_ _[name] = value
Properties
are an important object-oriented concept. Instances of a class often
need to expose two different kinds of attributes: those that hold
data and those that are computed on the fly with a suitable method,
whenever their values are required. If you expose the real attributes
directly and the computed attributes via methods, such as
getArea
, current implementation issues will appear
in the interface for your class and throughout the client code, which
should really be independent from such issues. And if you ever change
the implementation, you are in serious trouble.
The alternative of exposing everything via so-called accessor methods is also far from satisfactory. In this case, the code for your class fills up with highly repetitive boilerplate code such as:
def getWidth(self): return self.width
Even worse, your client code is cluttered with more verbose and less-readable statements such as:
r.setHeight(r.getHeight( )+1)
rather than more concise and readable statements such as:
r.height += 1
Moreover, the unnecessary calls to the accessor methods slow your code’s operation.
Properties let you have your cake and eat it too. Client code
accesses all attributes uniformly (e.g., r.width
,
r.area
) without caring or needing to know which
are real and which are computed on the fly. Your class just needs a
way to ensure that when client code accesses a computed attribute,
the right method is called, and its return value is taken as the
attribute’s value. For example:
>>> r = Rectangle(10, 20)
>>> print r.area
200
When client code accesses a real attribute, nothing special is needed.
With Python 2.2’s new-style classes, you can use the
built-in property
function to define properties.
You pass it the accessor functions for get and set operations,
optionally followed by one to use for deletions (an optional fourth
argument is the attribute’s documentation string).
You bind the return value to the name, in class scope, that you want
the client code to use when accessing the property on class
instances.
In classic classes, you can still have properties, but you need to
implement them yourself. When any code accesses an attribute that
doesn’t exist for an object, Python calls the
_ _getattr_ _
method for the class (if it exists)
with the attribute’s name as the argument. You just
need to test for the names of the properties that you are
implementing and delegate to the appropriate method, as shown in the
second solution. Whenever an attribute is set on your object (whether
the attribute exists or not), Python calls the _ _setattr_ _
method for the class (if it exists) with the
attribute’s name and the new value assigned to it as
arguments. Since _ _setattr_ _
is called for all
attribute settings, it must also deal with setting real attributes in
the normal ways (as items in self._ _dict_ _
).
Also, other methods in classes that implement _ _setattr_ _
often set items in self._ _dict_ _
directly to avoid triggering _ _setattr_ _
needlessly.
Properties are currently underdocumented. There is a minimal
description in Guido’s essay describing the
unification of types and classes (http://www.python.org/2.2/descrintro.html#property);
additional minimal information is available from the online help
system (help(property)
). However, by the time you
read this, the Language Reference will likely
have been updated.