[Python-Dev] 'hasattr' is broken by design

Raymond Hettinger raymond.hettinger at gmail.com
Mon Aug 23 21:47:00 CEST 2010


On Aug 23, 2010, at 7:22 AM, Yury Selivanov wrote:

> I know the issue has been discussed several times already, however I couldn't find any reasonable explanation of its strange behaviour.  The main problem with 'hasattr' function is that is swallows all exceptions derived from Exception class.  It's a good thing that it doesn't do that with BaseException as it was fixed not a long time ago, but it's definitely not enough.
> 
> First of all, this behaviour of 'hasattr' contradicts with the very core principle of python: "Errors should never pass silently."  And since 'hasattr' function is in builtins module and is a widely used function it impacts the whole language.
> 
> Secondly, take a look at the following:
> 
>>>> class Test:
>    ...     @property
>    ...     def attr(self):
>    ...         self['foo']
>    ... 
>>>> hasattr(Test(), 'attr')
>    False
> 
> There can be any exception instead of KeyError in the above snippet of code, but this small example shows how 'hasattr': misleadingly breaks the code logic (1) and masks bug (2).  And that's the simplest possible example, there are much more in real life.
> 
> While (1) is maybe acceptable for someone, there is no excuse for the (2).  Moreover, current 'hasattr' behaviour tremendously complicates use of '__getattribute__' magic.  And forget about importlib magic with LazyImports, one 'hasattr' ruins everything by catching ImportError.
> 
> 
> To conclude:
> 
> 1) I propose to change 'hasattr' behaviour in Python 3, making it to swallow only AttributeError exceptions (exactly like 'getattr').  Probably, Python 3.2 release is our last chance.
> 
> 2) If you afraid that this new behaviour will break too much python 2 code converted with 2to3, we can introduce another 'hasattr' function defined in 2to3 module itself, and make it imported automatically in all files passed through 2to3 transformation pipeline.  This new function will mimic 'hasattr' behaviour from python 2 and converted code should work as expected.


Thanks for the nice analysis and good example.

I disagree with the solution though.  If we want to see the exceptions associated 
with actually getting an attribute, then using getattr() instead is a perfectly 
reasonable solution that people can already use without a language change.

But hasattr() has a far different set of use cases, so we should explore
an alternate solution to the problem.  The usual reason that people use
hasattr() instead of getattr() is that they want to check for the presence of
of a method/attribute without actually running it, binding it, or triggering
any other behavior.

As your example shows, property() defeats this intent by actually executing
the code.   A better behavior would not run the code at all.  It would check
the dictionaries along the MRO but not execute any descriptors associated
with a given key.

IMO, this is a much better solution, more in line with known use cases
for hasattr().   If the proposed change when through, it would fail to
address the common use case and cause people to start writing their
own versions of hasattr() that just scan but do not run code.


Raymond




More information about the Python-Dev mailing list