Properties fun with 2.2

Greg Chapman glchapman at earthlink.net
Wed Dec 26 13:33:33 EST 2001


On Mon, 24 Dec 2001 09:27:11 +0000, Robin Becker <robin at jessikat.fsnet.co.uk>
wrote:

>very nice. I wondered if this stuff would be easy and it seems it can
>be. One of the things I need is a collection of properties ie a property
>
>P
>
>with its own attributes (probably properties themselves) which can be
>accessed as
>
>P[0]
>
>ie a thing similar to P, but allowed individual values where these
>differ from the aggregate. I wonder whether this is more easily done in
>2.2. It's quite hard in 2.1.

This farily easy in 2.2 (and also possible in earlier versions using a
__getattr__ handler), as long as you're willing to put up with the overhead of a
proxy class.  Specifically, getting a property P from an object returns a proxy
class which defines a __getitem__ which, when called, makes a call to a method
of the original object to get the actual result.  Here's an example from some
property extensions I've been experimenting with to try to incorporate some
Delphi-esque qualities to the new properties:

def _indexpropgeterr(proxy, key):
    raise TypeError("unindexable property")

def _indexpropseterr(proxy, key, value):
    raise TypeError("property doesn't support item assignment")

def _indexpropdelerr(proxy, key):
    raise TypeError("property doesn't support item deletion")

class _indexedPropProxy(object):
    def __init__(self, obj, indexedprop):
        self.obj = obj
        self.ifget = indexedprop.ifget or _indexpropgeterr
        self.ifset = indexedprop.ifset or _indexpropseterr
        self.ifdel = indexedprop.ifdel or _indexpropdelerr
# __iter__ gets special treatment because, if ifiter is not provided,
#  it may still be possible to iterate over this property using __getitem__
#  (provided that the property works with integer keys).
        ifiter = indexedprop.ifiter
        if ifiter:
            self.ifiter = ifiter
            self.__iter__ = lambda this: this.ifiter(this.obj)
        else:
            self.ifiter = None
    def __getitem__(self, key):
        return self.ifget(self.obj, key)
    def __setitem__(self, key, value):
        self.ifset(self.obj, key, value)
    def __delitem__(self, key):
        self.ifdel(self.obj, key)

class indexedproperty(property):
    def __init__(self, ifget=None, ifset=None, ifdel=None, 
                 ifiter=None, doc=None):
        property.__init__(
            self,
            fget=(lambda this: _indexedPropProxy(this, self)),
            doc=doc
        )
        self.ifget = ifget
        self.ifset = ifset
        self.ifdel = ifdel
        self.ifiter = ifiter

#################################

class Test(object):
    def __init__(self, items):
        self.__items = items
    def __getitem__(self, index):
        return self.__items[index]
    Items = indexedproperty(__getitem__)

# indexed properties which use __getitem__ (like above) are the equivalent of a 
# default array property in Delphi: test[0] is test.Items[0]

def RunTest():
    items = [1,2,3]
    test = Test()
#the following three loops all print the same thing
    for n in test.Items:
        print n
    for i in range(len(items)):
        print test.Items[i]
    for i in range(len(items)):
        print test[i]

---
Greg Chapman
    



More information about the Python-list mailing list