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@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