(original) (raw)
On Aug 23, 2010, at 1:03 PM, Guido van Rossum wrote:
But hasattr() has a far different set of use cases, so we should explorean alternate solution to the problem. The usual reason that people usehasattr() instead of getattr() is that they want to check for the presence ofof a method/attribute without actually running it, binding it, or triggeringany other behavior.As your example shows, property() defeats this intent by actually executingthe code. A better behavior would not run the code at all. It would checkthe dictionaries along the MRO but not execute any descriptors associatedwith a given key.IMO, this is a much better solution, more in line with known use casesfor hasattr(). If the proposed change when through, it would fail toaddress the common use case and cause people to start writing theirown versions of hasattr() that just scan but do not run code.
Hm... That sounds like scope creep to me. Properties are supposed to
be cheap and idempotent. Trying to figure out whether an attribute
exists without using \_\_getattr\_\_ is fraught with problems -- as soon
as a class overrides \_\_getattr\_\_ or \_\_getattribute\_\_ you're hosed
anyway.
I don't have a specific proposal in mind. My main questions are
\* Is there anything that hasattr(obj, key) can or should do that
can't already be done with getattr(obj, key, None)?
If not, do we really need to change anything?
\* Why do people typically use hasattr() instead getattr()?
Aren't they are really trying to just determine
whether a key exists somewhere in the MRO?
If so, then doing anything more than that is probably a surprise.
I know my own uses of hasattr() do not expect any exceptions at all.
It comes up in duck typing support different handling for different
types of inputs. If others are using it the same way, I think it is
unlikely that they have unittests to cover the possibility that
hasattr() would ever start raising exceptions.
I can vouch that the reason hasattr() catches too many exceptions is
that when I first added it (around 1990, I think :-) I wasn't very
attuned yet to the problems it could cause.
Fire-up the time machine?
Raymond
P.S. The current behavior seems to be deeply embedded:
- int PyObject\_HasAttr(PyObject \*o, PyObject \*attr\_name)
- Returns 1 if o has the attribute attr\_name, and 0 otherwise. This is equivalent to the Python expression hasattr(o, attr\_name). This function always succeeds.
- int PyObject\_HasAttrString(PyObject \*o, const char \*attr\_name)
- Returns 1 if o has the attribute attr\_name, and 0 otherwise. This is equivalent to the Python expression hasattr(o, attr\_name). This function always succeeds.