[Python-bugs-list] [ python-Bugs-729913 ] metaclasses, __getattr__, and special methods

SourceForge.net noreply@sourceforge.net
Tue, 29 Apr 2003 16:57:21 -0700


Bugs item #729913, was opened at 2003-04-29 16:57
Message generated for change (Tracker Item Submitted) made by Item Submitter
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=729913&group_id=5470

Category: Type/class unification
Group: None
Status: Open
Resolution: None
Priority: 5
Submitted By: Bjorn Pettersen (bpettersen)
Assigned to: Nobody/Anonymous (nobody)
Summary: metaclasses, __getattr__, and special methods

Initial Comment:
__getattr__ on metaclasses aren't called when it would 
seem "logical" <wink> for it to be. E.g.:

>>> class meta(type):
...   def __getattr__(cls, name):
...     if name == '__len__':
...        print "meta.__getattr__('__len__')"
...        return lambda: 42
...     else:
...        print 'meta.__getattr__', name
...        return name
...
>>> class S(object):
...   __metaclass__ = meta
...
>>> S.__len__()
meta.__getattr__('__len__')
42
>>> len(S)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: len() of unsized object
>>>

I was told that special method "foo(x, arg)" was 
implemented as
"type(x).__foo__(x, arg)", which doesn't seem to be the 
case always...
Compare:

>>> class meta(type):
...   def __len__(cls):
...     return 42
...
>>> class S(object):
...   __metaclass__ = meta
...
>>> S.__len__()
42
>>> len(S)
42
>>>

So, it looks like it's looking up __len__ in the 
metaclass, but not
falling back on __getattr__ when it isn't there? I've 
looked at the C
code and it seems like special methods each have their 
own way of
finding the function they're needing.

>From Alex Martelli:
Ah yes, I see now!  Yes, functions such as len() rely on 
slots in
the type object, e.g. as you've noticed:

> finding the function they're needing, e.g. for len, it 
looks like it
> uses:
> 
>   m = o->ob_type->tp_as_sequence;
>   if (m && m->sq_length)
>     return m->sq_length(o);
> 
>   return PyMapping_Size(o);

and the "incredibly complex thinking" (quoting from 
typeobject.c)
in update_one_slot doesn't seem to work except for 
operations the
which "the class overrides in its dict" (again from a 
comment in
typeobject.c, this one for fixup_slot_dispatchers).

The issue may be with _PyType_Lookup (again in the 
same ,c file),
which just gives up if it can't find a name somewhere 
along the mro
(it doesn't "look upwards" to the metaclass) while 
type_getattro
DOES work upwards on the metaclass too.  Hmmmm.  
I'm not sure I
really understand all that's going on here - it IS a rather 
hairy
zone of the code.  Maybe you can post this as a bug in 
2.3 beta 1
on sourgeforge (ideally showing where in the docs it 
defines the
semantics that it then doesn't respect) so we can get 
this looked
at by the few people who really DO grasp these parts...;-
).  There
is probably some sound implementation reason for the 
current
behavior, but if so it should be better documented, I 
think.

Back to me:
The point being that I haven't found any place in the 
documentation that defines what the attribute lookup is 
on new-style classes (and the C code is too hairy for 
me to understand :-)

As a special case of this problem, super() can't create 
an object which intercepts the special methods like it 
does for other methods, e.g.:

  super(MyCls, self).__getitem__(5)

works, but not

  super(MyCls, self)[5]

I don't know if that is intended or not, but it's not 
documented, though neither is exactly _what_ super is? 
(i.e. it looks like it's an object, that when you call a 
method, 'm', on it, uses the superclass method 'm', but 
the subclass versions of all other methods, although as 
above, not in all contexts, and I'm not sure whether 
you're supposed to be able to treat it as a first class 
object [pass as arg, return, etc])....

-- bjorn



----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=729913&group_id=5470