Reputation: 8999
Given the following class (with a buggy property) then what is the best foolproof way of checking that the bar
property exists?
class Foo(object):
@property
def bar(self):
raise AttributeError('unforeseen attribute error!')
Both hasattr
and getattr
fail and only dir
works:
foo = Foo()
print hasattr(foo, 'bar')
# False
try:
getattr(foo, 'bar')
print True
except AttributeError as e:
print False
# False
print 'bar' in dir(foo)
# True
The best all round solution I can think of is:
def robust_hasattr(obj, attr):
return hasattr(obj, attr) or attr in dir(obj)
Is there a better way?
Upvotes: 7
Views: 11088
Reputation: 281968
By the rules of Python, the bar
attribute does not exist. An object is considered to have an attribute if an attempt to access the attribute doesn't raise an exception.
If you want to use a different notion of whether an attribute exists, you can implement that notion yourself. For example, to check for the existence of an entry corresponding to bar
in the instance dict or one of the class dicts:
for obj in (foo,) + type(foo).__mro__:
if 'bar' in obj.__dict__:
print "It's there."
break
else:
print "Didn't find it."
Upvotes: 3
Reputation: 1124808
If you have a buggy property, fix the bug. If raising AttributeError
is a bug, then make the property not do that. Raising that exception is the way to signal that you should not be using that attribute.
Using dir()
can be a work-around, but it is not foolproof, as dir()
is a debugging aid that can both omit information and can be overridden by the object.__dir__
hook (giving your code another vector to introduce bugs). Then there is the possibility of a buggy object.__getattr__
hook, a buggy object.__getattribute__
hook, or even descriptors on the metaclass, all of which would not be detectable by using dir()
.
Since you are specifically looking for a property, look for the same attribute on the class of your object:
hasattr(foo, 'bar') or isinstance(getattr(type(foo), 'bar', None), property)
For your specific case, the above returns True:
>>> class Foo(object):
... @property
... def bar(self):
... raise AttributeError('unforeseen attribute error!')
...
>>> foo = Foo()
>>> hasattr(foo, 'bar') or isinstance(getattr(type(foo), 'bar', None), property)
True
because there indeed is such a property
object on the class.
Upvotes: 10