classification
Title: __getattr__ and metaclasses
Type: Stage:
Components: Interpreter Core Versions:
process
Status: closed Resolution: duplicate
Dependencies: Superseder:
Assigned To: Nosy List: georg.brandl, grodr, michele_s, mwh
Priority: normal Keywords:

Created on 2003-08-15 14:18 by grodr, last changed 2005-10-01 13:43 by georg.brandl. This issue is now closed.

Messages (4)
msg17782 - (view) Author: Gonçalo Rodrigues (grodr) Date: 2003-08-15 14:18
This came out from a thread in comp.lang.python: Here’s 
the reference:

http://groups.google.pt/groups?hl=pt-PT&lr=&ie=UTF-
8&threadm=md94jvccu02b9dv5890k34629rkot79roj%
404ax.com&rnum=6&prev=/groups%3Fq%3Dgon%25C3%
25A7alo%2Brodrigues%2Bgroup:comp.lang.python.*%
26hl%3Dpt-PT%26lr%3D%26ie%3DUTF-8%26group%
3Dcomp.lang.python.*%26selm%
3Dmd94jvccu02b9dv5890k34629rkot79roj%
25404ax.com%26rnum%3D6


Consider the following example (provided by M. 
Simionato)

>>> class M(type):
... 	def __getattr__(self, name):
... 		if name == '__iter__':
... 			return lambda self: iter([])
...
>>> class C(object):
... 	__metaclass__ = M
... 	
>>> C.__iter__
<function <lambda> at 0x0110E8F0>
>>> c = C()
>>> iter(c)
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
TypeError: iteration over non-sequence
>>>

This means that the iterator machinery does not check 
__getattr__ (or __getattribute__ for that matter – I 
have made the test). The Language Reference says:

“A class can implement certain operations that are 
invoked by special syntax (such as arithmetic operations 
or subscripting and slicing) by defining methods with 
special names.”

Which does not throw much light on the matter at hand. 
So there are two ways we can view the above:

(A) It’s a bug.

This is the one I favour ;-). Arguing by contradiction, 
not being considered a bug means that there is a very 
special distinction being made when it comes to 
attribute lookup of special names. 

I tend to follow the Gang of Four’s main 
injunction “Prefer composition to inheritance”. 
Composition is great in Python precisely because of the 
__getattr__ hook. Not being able to use __getattr__ in 
metaclasses to trap special names surely hampers that 
role somewhat.

(B) It’s not a bug.

Then at least I think that the documentation should be 
worded more accurately. Quoting A. Martelli on the same 
thread

“__getattr__ is not a BINDING of the special method, 
though it may be considered a DEFINITION of it, which is 
why the current phrase in the Language Reference is not 
100% correct and complete -- only 99.44%, and I agree 
that the remaining 0.56% _is_ a delicate defect in the 
documentation.”

With my best regards,
G. Rodrigues
msg17783 - (view) Author: Michele Simionato (michele_s) Date: 2003-08-21 10:16
Logged In: YES 
user_id=583457

Two comments:

1. The credit for discovering this "issue" goes to Bjorn
Pettersen, not to me.

2. The issue is NOT with metaclasses, the title should
be "special methods and __getattr__"

The metaclass works perfectly fine and

X.__iter__(x)

works. The problem is with the "iter" builtin, since

iter(x) DOES NOT call X.__iter__(x).

Same with str, len, etc.

Metaclasses are not guilty here!


  Michele
msg17784 - (view) Author: Michael Hudson (mwh) (Python committer) Date: 2004-03-01 11:12
Logged In: YES 
user_id=6656

> Metaclasses are not guilty here!

It's more complicated than that.

iter(o) does (roughly)

o->ob_type->tp_iter(o)

At class definition time, if the class defines __iter__, a
wrapper for it is stuffed into the type's tp_iter slot.  If
it doesn't, NULL is placed there instead.

What *could* be done is, if the *meta*class defines
__getattr__ or __getattribute__, all the tp_ slots could be
filled with a special wrapper that calls the generic
attribute getter.  But that would be quite a coding effort,
and these classes would have pretty atrocious performance.

And making this work when you assign to __getattribute__ on
the metaclass would be a truly crazy piece of code.

Or the docs could note this limitation.
msg17785 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-10-01 13:43
Logged In: YES 
user_id=1188172

Duplicate of #729913.
History
Date User Action Args
2003-08-15 14:18:25grodrcreate