On 25 Nov 2004 06:35:23 -0800, [EMAIL PROTECTED] (Sebastien Boisgerault) wrote:

>Peter Maas <[EMAIL PROTECTED]> wrote in message news:<[EMAIL PROTECTED]>...
>> Sebastien Boisgerault schrieb:
>> > I wonder if the following quotation from the Python Reference Manual 
>> > (release 2.3.3) about operator overloading is true :
>> > 
>> > "For example, if a class defines a method named __getitem__(), and x
>> > is an instance of this class, then x[i] is equivalent to
>> > x.__getitem__(i)"
>>  [...]
>> >>>>from Numeric import *
>> >>>>a = array([0.5])
>> >>>>a[0]
>> > 
>> > 0.5
>> > 
>> > but
>> > 
>> > 
>> >>>>a.__getitem__(0)
>> > 
>> > Traceback (most recent call last):
>> >   File "<stdin>", line 1, in ?
>> > AttributeError: __getitem__
>> 
>> The quotation above is true. Short form:
>> 
>> IF __getitem__ in dict THEN [] works.
>
>Not exactly the same assertion: 
>replace "__getitem__ in dict" by "__getitem__ in the class dict" 
>and more importantly "[] works" by "[] and __getitem__" are *equivalent*.
>
>Here, "__getitem__" does belongs to type(a).__dict__,
>so "[]" and "__getitem__" should work exactly the same 
>according to the reference, but they don't.
>
>> [...]
>> but this is not what the Python Reference Manual says. Im not a
>> Numeric expert but AFAIK Numeric arrays are basically C arrays
>> having [] intrinsically so there's no need no deliver it via
>> __getitem__.
>
>I would buy your argument if I couldn't find the "__getitem__" method.
>But it does exist ! Except that it is hidden is the class __dict__ and
>apparently cannot be recovered from the instance.__getitem__ call ...
> 
>Thanks for your help,
>
>SB
I believe the new style classes require looking for a descriptor (which
includes functions, which become bound methods via their descriptor nature)
with the attribute name given, before grabbing something from the instance dict.
Otherwise instance attributes would always shadow corresponding method or 
property
names, and those things wouldn't work, or would work as in the old style 
classes.

Therefore looking for __getitem__ is a little trickier than it might seem.
It has to work like any other name, so a.__getitem__ can't be treated 
differently from a.foo.

So as you noticed, the first place to look is in type(a).__dict__ (which is
also an attribute lookup BTW, with name '__dict__' which could be a descriptor
too, but we'll ignore that for the moment. See further below for that).

Consider that given

 >>> import Numeric
 >>> a = Numeric.array([0.5])
 >>> a
 array([ 0.5])

this

 >>> a[0]
 0.5

produces the same result as this

 >>> type(a).__dict__['__getitem__'].__get__(a, type(a)).__call__(0)
 0.5

So what happens when we look for type(a).__dict__? '__dict__' is just a name,
so we have to look for a method or property in the chain of base classes.
The buck presumably stops at some base class descriptor named __dict__, if any,
and that descriptor, if present, determines what you get. The chain of search
for type(a).__dict__ presumably starts looking in type(type(a)).__dict__, but

 >>> type(type(a))
 <type 'type'>

is already at the end of the chain.

 >>> type(type(a)).__dict__
 <dictproxy object at 0x0090B4D0>

Remember, we're going to look _in_ the __dict__, not _for_ it here ;-)

But this has already been processed through the attribute magic, so to see
what '__dict__' is without that processing, we use the proxy to look it up:

 >>> type(type(a)).__dict__['__dict__']
 <attribute '__dict__' of 'type' objects>

Which is a descriptor if it has a __get__ method:
 >>> type(type(a)).__dict__['__dict__'].__get__
 <method-wrapper object at 0x0090B4D0>

Sure enough, so we pass type(a) and its type to that, and get back 
type(a).__dict__ the long way:

 >>> type(type(a)).__dict__['__dict__'].__get__(type(a), type(type(a)))
 <dictproxy object at 0x009015B0>

now we can look for __getitem__ in that:
 >>> type(type(a)).__dict__['__dict__'].__get__(type(a), 
 >>> type(type(a)))['__getitem__']
 <slot wrapper '__getitem__' of 'array' objects>

Which being the function of a method, should have a descriptor's __get__ 
method, by which
to become a bound method:

 >>> type(type(a)).__dict__['__dict__'].__get__(type(a), 
 >>> type(type(a)))['__getitem__'].__get__
 <method-wrapper object at 0x0090B4D0>

So we pass it the instance and its type:
 >>> type(type(a)).__dict__['__dict__'].__get__(type(a), 
 >>> type(type(a)))['__getitem__'].__get__(a, type(a))
 <method-wrapper object at 0x009015B0>

Which should have a __call__ method if it's callable:
 >>> type(type(a)).__dict__['__dict__'].__get__(type(a), 
 >>> type(type(a)))['__getitem__'].__get__(
                                                                             a, 
type(a)).__call__
 <method-wrapper object at 0x0090B4D0>

Which we can call with the index
 >>> type(type(a)).__dict__['__dict__'].__get__(type(a), 
 >>> type(type(a)))['__getitem__'].__get__(
                                                                             a, 
type(a)).__call__(0)
 0.5

Fortunately, we don't normally have to think about all that when we write

 >>> a[0]
 0.5

;-)

Caveat: this is not based on reading the code internals, so I could be 
misinterpreting surface
appearances, but at least it ought to be clear that a[0] involves a lot of 
dynamic decisions that
might ordinarlily not be taken, but which must be allowed for in looking for an 
innocent method
like __getitem__ ;-)

[Hm, just looking in oubox: this apparently didn't go out the other day.]

Regards,
Bengt Richter
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to