I'm struggling with __getattr__
. I have a complex recursive codebase, where it is important to let exceptions propagate.
class A(object):
@property
def a(self):
raise AttributeError('lala')
def __getattr__(self, name):
print('attr: ', name)
return 1
print(A().a)
Results in:
('attr: ', 'a')
1
Why this behaviour? Why is no exception thrown? This behaviour is not documented (__getattr__
documentation). getattr()
could just use A.__dict__
. Any thoughts?
__getattr__(self, name) Is an object method that is called if the object's properties are not found. This method should return the property value or throw AttributeError . Note that if the object property can be found through the normal mechanism, it will not be called.
Yes, compared to the direct conventional method of accessing an attribute of a given object, the performance of getattr() is slower.
To avoid the AttributeError in Python code, a check should be performed before referencing an attribute on an object to ensure that it exists. The Python help() function can be used to find out all attributes and methods related to the object. To resolve the AttributeError , a try-except block can be used.
Python getattr() function is used to get the value of an object's attribute and if no attribute of that object is found, default value is returned.
Using __getattr__
and properties in the same class is dangerous, because it can lead to errors that are very difficult to debug.
If the getter of a property throws AttributeError
, then the AttributeError
is silently caught, and __getattr__
is called. Usually, this causes __getattr__
to fail with an exception, but if you are extremely unlucky, it doesn't, and you won't even be able to easily trace the problem back to __getattr__
.
Unless your property getter is trivial, you can never be 100% sure it won't throw AttributeError
. The exception may be thrown several levels deep.
Here is what you could do:
__getattr__
in the same class.try ... except
block to all property getters that are not trivialAttributeError
@property
decorator, which catches AttributeError
and re-throws it as RuntimeError
.See also http://blog.devork.be/2011/06/using-getattr-and-property_17.html
EDIT: In case anyone is considering solution 4 (which I don't recommend), it can be done like this:
def property_(f):
def getter(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2]
return property(getter)
Then use @property_
instead of @property
in classes that override __getattr__
.
I just changed the code to
class A(object):
@property
def a(self):
print "trying property..."
raise AttributeError('lala')
def __getattr__(self, name):
print('attr: ', name)
return 1
print(A().a)
and, as we see, indeed the property is tried first. But as it claims not to be there (by raising AttributeError
), __getattr__()
is called as "last resort".
It is not documented clearly, but can maybe be counted under "Called when an attribute lookup has not found the attribute in the usual places".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With