(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 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.

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 \*oPyObject \*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.