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