I have had no doubt using hasattr. But recently, I tripped on a small piece of stone.
I want to check whether foo attribute exists in the derived class. If hasattr is used, the derived class property foo is executed unnecessarily. So I started thinking that if there’s a possibility that the attribute to check is a property, it should be replaced with the “x in dir()” idiom (although I don’t like such mixtures)…
class Base(object):
def __init__(self):
## According to the Python documentation:
## <https://docs.python.org/3/library/functions.html#hasattr>
## This is implemented by calling getattr(object, name) and seeing
## whether it raises an AttributeError or not.
print(hasattr(self, 'foo')) # False. foo<property> is called.
print('foo' in dir(self)) # True. foo<property> is not called.
class Device(Base):
def __init__(self):
super().__init__()
self.bar = 0
@property
def foo(self):
print("foo is called.")
return self.bar
Device()
My question is, is there any problem in replacing hasattr with x in dir()?
If no, a further question I would like to ask is why hasattr was not implemented by x in dir().
Seems like that’s indeed the best solution availible; I explored a few other possible approaches, but they all were either less clean, simple and robust, or still executed the property code.
That wouldn’t completely nicely play with non-property descriptors like functools.cached_property. The only truly robust way is to call hasattr(self, name) (and even that assumes that the object is sane and doesn’t delete the attribute after an access, doesn’t randomly decide what attributes exists, isn’t modified concurrently, …). In general, you should assume that your properties are accessed repeatably and they should not have side effects.
For added fun: a property can itself emit AttributeError. One con
imagine doing so deoiberately for some property which is only present in
some circumstances.
This bites me frequently during dev, as any attribute related error in
the property implementation (including deeper things which it calls)
causes the property to appear not to exist. Same with a __getattr__. I
have a few classes with dynamic attributes depending on their state.