This recipe allows you to declare properties as inner classes. It's based combining a class-based idea proposed by Michael Urman on python-dev (http://mail.python.org/pipermail/python-dev/2005-October/057372.html) with late-binding properties (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/408713)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class Property(object): class __metaclass__(type): def __init__(cls, name, bases, dct): for fname in ['get', 'set', 'delete']: if fname in dct: setattr(cls, fname, staticmethod(dct[fname])) def __get__(cls, obj, objtype=None): if obj is None: return cls fget = getattr(cls, 'get') return fget(obj) def __set__(cls, obj, value): fset = getattr(cls, 'set') fset(obj, value) def __delete__(cls, obj): fdel = getattr(cls, 'delete') fdel(obj)
This recipe has a few nice properties. First, it groups all the functions that are part of the property together, and puts the label "Property" at the beginning of this group, instead of at the end as the builtin property() does:
<pre>py> class Temp(object): ... class foo(Property): ... """The foo property""" ... def get(self): ... return 'foo:%s' % self._foo ... def set(self, val): ... self._foo = val ... py> t = Temp() py> t.foo = 42 py> t.foo 'foo:42'</pre>
It also has the benefit that, unlike with the builtin property(), individual functions (e.g. get() or set()) can be easily added or overridden in a subclass without having to repeat the entire property definition:
<pre>py> class SubTemp(Temp): ... class foo(Temp.foo): ... def set(self, val): ... self._foo = val + 1 ... def delete(self): ... del self._foo ... py> s = SubTemp() py> s.foo = 42 py> s.foo 'foo:43' py> del s.foo py> s.foo Traceback (most recent call last): ... AttributeError: 'SubTemp' object has no attribute '_foo'</pre>
It may be confusing that the "self" parameter in the get(), set() and delete() methods is the Temp object, and not the Property object. To get the Property class object as another argument, you could take out the __init__ method, which does the staticmethod() wrapping.