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@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/
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe:
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com