Overloading operators on the rigth.

Bengt Richter bokr at oz.net
Wed Oct 13 12:52:57 CEST 2004


On Wed, 13 Oct 2004 10:18:28 +0200, aleaxit at yahoo.com (Alex Martelli) wrote:

>Bengt Richter <bokr at oz.net> wrote:
>   ...
>> >>>> class foo:
>> >...   def __getattr__(self, name):
>   ...
>> On my system, (NT4, python 2.3.2) what really happens for newstyle classes
>> isn't apparent from that experiment:
>
>Heh, of course not -- the main difference between old-style and
>new-style classes is that, for the latter, implicit lookup of special
>methods when needed for operations starts with the *class*, not with the
Well, it always starts with the class if there's no attribute on the instance,
but the key is whether the "implicit lookup" for methods needed for operations
uses an overridable __getattribute__ somewhere, or just chases down the bases
chain looking for the actual __gt__ or whatever, not allowing a hook to synthesize
a result.
>*instance*.  This is a fix of a long-standing design bug, because the
>old rule was just impossible to apply consistently -- if calling X meant
>looking for X.__call__ rather than X.__class__.__call__, how could you
>ever instantiate X by calling it, when X is a class whose _instances_
>are meant to be callable, for example?
>
>Since this has been discussed many times on this group, as well as in
>presentations, articles, websites, books, ..., I didn't think it was
>necessary to dwell on the issue again, considering it really did not
>affect the answer requested by the original poster; apparently I was
>wrong in so thinking.  _Sigh_ -- next time somebody criticizes my posts
>as being too long, I'll retort that of course they are, if I must keep
>repeating such issues!-)
>
I have suspected you of having voice recognition input ;-)
I don't type that fast, unfortunately.

>I just used an old-style class because, that way, I could show the
>lookups happening without having to code a custom metaclass.  The
Could you show such a metaclass? I have tried a few things and not succeeded.
It almost makes me think there is no place to hook the internal __getattribute__
that looks for the methods for code generated from 12<f and the like.
I'd be interested in seeing it.

>special names being looked for are just the same, whether for classic or
                                    ^^^^^^^^^^^^^^ but maybe not the same order, see below 
>newstyle classes, anyway -- all that changes is _where_ they're looked
>up (starting on the instance, vs starting on the class).
>
> 
>>  >>> class foo(object):
>>  ...   def __getattr__(self, name):
>>  ...     print 'looking for %r' % name
>>  ...     raise AttributeError, name
>>  ...
>>  >>> f=foo()
>>  >>> 12 < f
>>  True
>> 
>> The old style does work much the same (but note the default end result ;-)
>> Is that a 2.3.2 bug?
>
>No bug: comparisons between objects of disparate types give essentially
>arbitrary results (repeatable within one run, but, at least in theory,
>potentially different among runs of the same program, I believe).
>
I tried a class with all the methods explicit, and then removed them:

 >>> class foo(object):
 ...     def __gt__(self, other): print 'gt'
 ...     def __coerce__(self, other): print 'coerce'
 ...     def __cmp__(self, other): print 'cmp'
 ...
 >>> f=foo()
 >>> 12<f
 gt

Ok, so it looked for __gt__ first. Remove that.
>>> del foo.__gt__

>>> 12<f
cmp
It seems to have looked for __cmp__ second, unlike in your post:

+---<from your post>------------------
Here's how to find out:

>>> class foo:
...   def __getattr__(self, name):
...     print 'looking for %r' % name
...     raise AttributeError, name
... 
>>> f=foo()
>>> 12 < f
looking for '__gt__'
looking for '__coerce__'
looking for '__cmp__'
True
>>> 
+-------------------------------------

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: an integer is required
(didn't like none back from cmp, presumably)

Ok, remove __cmp__
>>> del foo.__cmp__

One left:
>>> 12<f
coerce
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: __coerce__ didn't return a 2-tuple

NBD, but perhaps the order change is a version difference?
Not that the OP was asking any of this ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list