Recipe20.5.Using One Method as Accessorfor Multiple Attributes


Recipe 20.5. Using One Method as Accessorfor Multiple Attributes

Credit: Raymond Hettinger

Problem

Python's built-in property descriptor is quite handy but only as long as you want to use a separate method as the accessor of each attribute you make into a property. In certain cases, you prefer to use the same method to access several different attributes, and property does not support that mode of operation.

Solution

We need to code our own custom descriptor, which gets the attribute name in _ _init_ _, saves it, and passes it on to the accessors. For convenience, we also provide useful defaults for the various accessors. You can still pass in None explicitly if you want to forbid certain kinds of access but the default is to allow it freely.

class CommonProperty(object):     def _ _init_ _(self, realname, fget=getattr, fset=setattr, fdel=delattr,                  doc=None):         self.realname = realname         self.fget = fget         self.fset = fset         self.fdel = fdel         self._ _doc_ _ = doc or ""     def _ _get_ _(self, obj, objtype=None):         if obj is None:             return self         if self.fget is None:             raise AttributeError, "can't get attribute"         return self.fget(obj, self.realname)     def _ _set_ _(self, obj, value):         if self.fset is None:             raise AttributeError, "can't set attribute"         self.fset(obj, self.realname, value)     def _ _delete_ _(self, obj):         if self.fdel is None:             raise AttributeError, "can't delete attribute"         self.fdel(obj, self.realname, value)

Discussion

Here is a simple example of using this CommonProperty custom descriptor:

class Rectangle(object):     def _ _init_ _(self, x, y):         self._x = x                    # don't trigger _setSide prematurely         self.y = y                     # now trigger it, so area gets computed     def _setSide(self, attrname, value):         setattr(self, attrname, value)         self.area = self._x * self._y     x = CommonProperty('_x', fset=_setSide, fdel=None)     y = CommonProperty('_y', fset=_setSide, fdel=None)

The idea of this Rectangle class is that attributes x and y may be freely accessed but never deleted; when either of these attributes is set, the area attribute must be recomputed at once. You could alternatively recompute the area on the fly each time it's accessed, using a simple property for the purpose; however, if area is accessed often and sides are changed rarely, the architecture of this simple example obviously can be preferable.

In this simple example of CommonProperty use, we just need to be careful on the very first attribute setting in _ _init_ _: if we carelessly used self.x = x, that would trigger the call to _setSide, which, in turn, would try to use self._y before the _y attribute is set.

Another issue worthy of mention is that if any one or more of the fget, fset, or fdel arguments to CommonProperty is defaulted, the realname argument must be different from the attribute name to which the CommonProperty instance is assigned; otherwise, unbounded recursion would occur on trying the corresponding operation (in practice, you'd get a RecursionLimitExceeded exception).

See Also

The Library Reference and Python in a Nutshell documentation for built-ins getattr, setattr, delattr, and property.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net