Propert handler question

Alex Martelli aleax at aleax.it
Fri Oct 31 04:18:42 EST 2003


 user at domain.invalid wrote:

> Is there a way, from within a getter/setter method
> linked to a propert, that I can tell which property
> triggered the method?
> 
> This would be great, because I need a great number
> of these properties, each having only slightly different
> actions, which could be triggered correctly if
> I knew the name of the property that was being accessed.

So use a closure.  E.g, trivial example:

def prop(name):
    def getter(self):
        print 'getting', name
        return getattr(self, '_'+name)
    def setter(self, value):
        print 'setting', name, 'to', value
        return setattr(self, '_'+name, value)
    return getter, setter

class WithProperties(object):
    foo = property(*prop('foo'))
    bar = property(*prop('bar'))
    baz = property(*prop('baz'))

w = WithProperties()
w.foo = w.bar = 23
print w.foo, w.bar

will emit:

[alex at lancelot bo]$ python proe.py
setting foo to 23
setting bar to 23
getting foo
23 getting bar
23


Yes, this does require a double specification of the name -- as
an argument to prop AND as the thing you assign to in classbody.

Avoiding this requires black or at least dark-grayish magic, such
as (I've seen others already suggest somewhat-more-magic-yet
solutions requiring both custom metaclasses and custom descriptors,
this one at least makes do with a custom metaclass and a descriptor
_helper_ that gets turned into an ordinary property descriptor:-)...:

class magicprop(object):
    def __init__(self, name=''): self.name = name
    def getter(self, other):
        print 'getting', self.name
        return getattr(other, '_'+self.name)
    def setter(self, other, value):
        print 'setting', self.name, 'to', value
        return setattr(other, '_'+self.name, value)

class magicmeta(type):
    def __new__(mcl, clasname, clasbase, clasdict):
        for n, v in clasdict.items():
            if not isinstance(v, magicprop): continue
            v.name = n
            clasdict[n] = property(v.getter, v.setter)
        return type.__new__(mcl, clasname, clasbase, clasdict)

class magic: __metaclass__ = magicmeta

class WithProperties(magic):
    foo = magicprop()
    bar = magicprop()
    baz = magicprop()

w = WithProperties()
w.foo = w.bar = 23
print w.foo, w.bar


this gives the same output as before.

If you do a lot of this you probably don't want to code the
getters and setters right inside the magicprop helper, of
course, but rather code them in the target class and pass
them to magicprop as you'd normally pass them to property.
No problem, actually...:

class magicprop(object):
    def __init__(self, getter, setter, name=''):
        self.name = name
        self.getter = getter
        self.setter = setter
    def get(self, other):
        return self.getter(other, self.name)
    def set(self, other, value):
        return self.setter(other, self.name, value)

class magicmeta(type):
    def __new__(mcl, clasname, clasbase, clasdict):
        for n, v in clasdict.items():
            if not isinstance(v, magicprop): continue
            v.name = n
            clasdict[n] = property(v.get, v.set)
        return type.__new__(mcl, clasname, clasbase, clasdict)

class magic: __metaclass__ = magicmeta

class WithProperties(magic):
    def getter(self, name):
        print 'getting', name
        return getattr(self, '_'+name)
    def setter(self, name, value):
        print 'setting', name, 'to', value
        setattr(self, '_'+name, value)
    foo = magicprop(getter, setter)
    bar = magicprop(getter, setter)
    baz = magicprop(getter, setter)

w = WithProperties()
w.foo = w.bar = 23
print w.foo, w.bar


and again the output is as usual.


Alex





More information about the Python-list mailing list