I’m writing a python class that uses __setattr__ and __getattr__ to provide custom attribute access.
However, some attributes can’t be handled in a generic way, so I was hoping to use descriptors for those.
A problem arises in that for a descriptor, the descriptor’s __get__ will be invoked in favour of the instances __getattr__, but when assigning to an attribute, __setattr__ will be invoked in favour of the descriptors __set__.
An example:
class MyDesc(object):
def __init__(self):
self.val = None
def __get__(self, instance, owner):
print "MyDesc.__get__"
return self.val
def __set__(self, instance, value):
print "MyDesc.__set__"
self.val = value
class MyObj(object):
foo = MyDesc()
def __init__(self, bar):
object.__setattr__(self, 'names', dict(
bar=bar,
))
object.__setattr__(self, 'new_names', dict())
def __setattr__(self, name, value):
print "MyObj.__setattr__ for %s" % name
self.new_names[name] = value
def __getattr__(self, name):
print "MyObj.__getattr__ for %s" % name
if name in self.new_names:
return self.new_names[name]
if name in self.names:
return self.names[name]
raise AttributeError(name)
if __name__ == "__main__":
o = MyObj('bar-init')
o.bar = 'baz'
print o.bar
o.foo = 'quux'
print o.foo
prints:
MyObj.__setattr__ for bar
MyObj.__getattr__ for bar
baz
MyObj.__setattr__ for foo
MyDesc.__get__
None
The descriptor’s __set__ is never called.
Since the __setattr__ definition isn’t just overriding behaviour for a limited set of names, there’s no clear place that it can defer to object.__setattr__
Is there a recommended way to have assigning to attributes use the descriptor, if available, and __setattr__ otherwise?
update
Revisiting this answer more than 10 years later, I realized that I included a recipe, but did not explain the outstanding behavior the OP met: a descritpor’s
__get__is called before an instance’s__getattr__, yes. But the actual converse to__setattr__is__getattribute__, not__getattr__. Insideobject‘s__getattribute__is where is the code with the search order for attributes in first place, and the code responsible for calling a descriptor’s__get__itself.__getattr__is called by the code in__getattribute__itself as a last resort, after inspecting any possible descriptors,__slots__, and the instance’s__dict__.original answer: just do what the OP wants to achieve
I think I’d approach this by having a mechanism to automatically mark which are the
descriptors in each class, and wrap the
__setattr__in a way that it’d callobject’s normal behavior for those names.
This can be easily achieved with a metaclass (and a decorator for
__setattr__