[Python-Dev] 'hasattr' is broken by design
Michael Foord
fuzzyman at voidspace.org.uk
Mon Aug 23 21:56:27 CEST 2010
On 23/08/2010 22:47, Raymond Hettinger wrote:
> 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.
>
It would be backwards incompatible with usage of hasattr for dynamically
created 'members' using __getattr__ though. For what it's worth I
*agree* with you [1], but for better or worse hasattr / getattr trigger
code execution and hasattr can return True for dynamically created
members. Something to be revisited for Python 4 perhaps.
Michael Foord
[1] A while ago I wrote a couple of blog entries on fetching docstrings
from members without triggering code execution. It is surprisingly
convoluted and even my final code had corner cases it didn't handle:
http://www.voidspace.org.uk/python/weblog/arch_d7_2009_05_16.shtml#e1090
http://www.voidspace.org.uk/python/weblog/arch_d7_2009_06_20.shtml#e1103
> Raymond
>
>
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.uk
--
http://www.ironpythoninaction.com/
More information about the Python-Dev
mailing list