operator overloading
Lenard Lindstrom
len-l at telus.net
Wed Apr 4 19:59:49 EDT 2007
looping wrote:
> Hi,
> for the fun I try operator overloading experiences and I didn't
> exactly understand how it works.
>
> Here is my try:
>>>> class myint(int):
> def __pow__(self, value):
> return self.__add__(value)
>
>>>> a = myint(3)
>>>> a ** 3
> 6
>
> OK, it works. Now I try different way to achieve the same result but
> without much luck:
>
>>>> class myint(int):
> pass
>>>> myint.__pow__ = myint.__add__
>
> or:
>>>> class myint(int):
> __pow__ = int.__add__
>
> or:
>>>> class myint(int):
> pass
>>>> a.__pow__ = a.__add__
>
> but for every try the result was the same:
>>>> a = myint(3)
>>>> a ** 3
> 27
>
> Why it doesn't works ?
>
This may, or may not be a bug, depending on ones interpretation.
int.__pow__ is a ternary operation while int.__add__ is binary. For a
Python function, like def __pow__(self, ...), the interpreter can't
properly check if the method takes the correct number of arguments, so
accepts it without question. But since int.__add__ is a slot wrapper the
interpreter can tell, and chokes. So it just falls back to using
int.__pow__.
Now for a more detailed explanation. Each Python type, and class, has a
vtable of C function pointers for all the operations the interpreter
understands. A type only defines functions for those operations it
supports. The remaining pointer are null. These functions are known as
slot functions, a slot being a C structure field in a Python object
instance (Types are instances too!). The int type has slot functions for
binary + and ternary ** for instance, but none for container [].
To make the slot mechanism visible to Python, allowing operator
overloading, generic wrapper slot functions and slot wrapper methods are
used. When a special method, such as __add__ or __pow__, is added to a
class, either in the class declaration or later by attribute assignment,
the corresponding slot is set to point to a generic wrapper that will
actually call the special method. When a builin type, like int, has a
type specific slot function for a particular operation, a corresponding
slot wrapper is added to the type that can call the slot function. So
not only can one define addition for a class by having an __add__ method
(via generic wrappers), but one can call the __add__ method on an int
(via slot wrappers).
Now for the specific case of myint. Not all slot functions are created
equal. The binary + slot function takes two arguments, the ternary **
slot function three. So int's corresponding __add__ and __pow__ slot
wrapper methods also differ. When subclassing int all goes well:
class myint(int):
pass
The myint class inherits int's slot functions for arithmetic, and those
functions can be called by int's slot wrappers, such as __add__ and
__pow__. But the following is unexpected:
myint.__pow__ = myint.__add__
Here __pow__ calls a slot function taking three arguments, __add__ calls
one taking two. What follows is probably unplanned, no exception is
raised. myint's ** slot function is not replaced with a generic wrapper
that will call the __pow__ method. Instead myint keeps the slot function
inherited from int. So myint may have a __pow__ method that adds, but it
is never called when doing a **.
Curiously C PyPy does what was expected:
>pypy-c.exe
Python 2.4.1 (pypy 1.0.0 build 41438) on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>> class MyInt(int):
.... pass
....
>>>> MyInt.__pow__ = MyInt.__add__
>>>> mi=MyInt(12)
>>>> mi**2
14
>>>>
Lenard Lindstrom
More information about the Python-list
mailing list