Welcome, guest | Sign In | My Account | Store | Cart

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)

Python, 17 lines
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')

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.

1 comment

tharakawick 16 years ago  # | flag

A simpler implementation. If you are not looking for properties that are overrideable method by method, then this can be implemented in an even simpler way:

Property = None

class Property (object):
    class __metaclass__ (type):
        def __new__(cls, name, bases, dct):
            if Property in bases:
                get = dct.get('get')
                set = dct.get('set')
                delete = dct.get('delete')
                doc = dct.get('__doc__')
                return property(get, set, delete, doc)
            return type.__new__(cls, name, bases, dct)

This Property class can be used just like the earlier version. The advantages of this approach are:

1) Shorter code.

2) behaves identical to to the built-in property() method. The property objects created are the same as if done by calling property(), since it the implementation uses the property itself.

The disadvantage is of course, you cannot override get, set, or delete separately.

Created by Steven Bethard on Mon, 17 Oct 2005 (PSF)
Python recipes (4591)
Steven Bethard's recipes (7)

Required Modules

  • (none specified)

Other Information and Tasks