operator overloading

L

looping

Hi,
for the fun I try operator overloading experiences and I didn't
exactly understand how it works.

Here is my try: def __pow__(self, value):
return self.__add__(value)
6

OK, it works. Now I try different way to achieve the same result but
without much luck:
__pow__ = int.__add__

or:
but for every try the result was the same:27

Why it doesn't works ?
 
Z

Ziga Seilnacht

looping said:
Hi,
for the fun I try operator overloading experiences and I didn't
exactly understand how it works.

Here is my try:

def __pow__(self, value):
return self.__add__(value)


6

OK, it works. Now I try different way to achieve the same result but
without much luck:


__pow__ = int.__add__

or:


but for every try the result was the same:>>> a = myint(3)
27

Why it doesn't works ?

This looks like a bug in Python. It works for all the other
operators:
.... __sub__ = int.__add__
.... __mul__ = int.__add__
.... __div__ = int.__add__
.... __truediv__ = int.__add__
.... __floordiv__ = int.__add__
.... __mod__ = int.__add__
.... __lshift__ = int.__add__
.... __rshift__ = int.__add__
.... __and__ = int.__add__
.... __xor__ = int.__add__
.... __or__ = int.__add__
.... __pow__ = int.__add__
....74088

You should submit a bug report to the bug tracker:

http://sourceforge.net/bugs/?group_id=5470

Ziga
 
7

7stud

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)


6

OK, it works. Now I try different way to achieve the same result but
without much luck:




or:>>> class myint(int):

__pow__ = int.__add__

or:


but for every try the result was the same:>>> a = myint(3)

27

Why it doesn't works ?

Look at the output of the following code:

class myint(int):
pass

print myint.__pow__
print myint.__add__

-----output:-----
<slot wrapper '__pow__' of 'int' objects>
<slot wrapper '__add__' of 'int' objects>

That shows that the names '__pow__' and '__add__' are __slots__.
According to Python in a Nutshell(p.102), a name that is a slot can
only be "bound"(i.e. assigned to) inside the class body. Any later
attempt to assign something to a name that is a slot has no effect.
In your case, I think that means that the only place you can assign
something to an int slot is inside the int class.

As an alternative, you could override __pow__(). Something like this:

class myint(int):
def __pow__(self, num):
return super(myint, self).__add__(num)

n = myint(3)
print n**3

---output:---
6

That doesn't attempt to reassign something to the slot int.__pow__;
rather it creates a new __pow__ name that hides int.__pow__.
 
7

7stud

Look at the output of the following code:

class myint(int):
pass

print myint.__pow__
print myint.__add__

-----output:-----
<slot wrapper '__pow__' of 'int' objects>
<slot wrapper '__add__' of 'int' objects>

That shows that the names '__pow__' and '__add__' are __slots__.
According to Python in a Nutshell(p.102), a name that is a slot can
only be "bound"(i.e. assigned to) inside the class body. Any later
attempt to assign something to a name that is a slot has no effect.
In your case, I think that means that the only place you can assign
something to an int slot is inside the int class.

As an alternative, you could override __pow__(). Something like this:

class myint(int):
def __pow__(self, num):
return super(myint, self).__add__(num)

n = myint(3)
print n**3

---output:---
6

That doesn't attempt to reassign something to the slot int.__pow__;
rather it creates a new __pow__ name that hides int.__pow__.

Hmmm...after reading Ziga's post and doing some more testing, I don't
think __slots__ has anything to do with it.
 
7

7stud

According to Python in a Nutshell(p.102), a name that is a slot can
only be "bound"(i.e. assigned to) inside the class body.

Upon closer reading, it actually says that the name "__slots__" has to
be bound inside the class body for the slot restrictions to take
effect.
 
T

Terry Reedy

| This looks like a bug in Python. It works for all the other
| operators:
|
| >>> class MyInt(int):
| ... __sub__ = int.__add__
| ... __mul__ = int.__add__
| ... __div__ = int.__add__
| ... __truediv__ = int.__add__
| ... __floordiv__ = int.__add__
| ... __mod__ = int.__add__
| ... __lshift__ = int.__add__
| ... __rshift__ = int.__add__
| ... __and__ = int.__add__
| ... __xor__ = int.__add__
| ... __or__ = int.__add__
| ... __pow__ = int.__add__
| ...
| >>> i = MyInt(42)
| >>> i + 3
| 45
| >>> i - 3
| 45
| >>> i * 3
| 45
| >>> i / 3
| 45
| >>> i // 3
| 45
| >>> i % 3
| 45
| >>> i << 3
| 45
| >>> i >> 3
| 45
| >>> i & 3
| 45
| >>> i ^ 3
| 45
| >>> i | 3
| 45
| >>> i ** 3
| 74088
|
| You should submit a bug report to the bug tracker:
|
| http://sourceforge.net/bugs/?group_id=5470

Nice test. I thought maybe __pow__ might be different in not having a
reverse form, but indeed, int has an __rpow__ method.

Before submitting, make sure that this does not work in 2.5, and then say
so in the bug report. In fact, copy the version/system info that the
interactive interpreter puts up when it starts.

tjr
 
S

sjdevnull

| This looks like a bug in Python. It works for all the other
| operators: [SNIP]
| >>> i ** 3
| 74088
|
| You should submit a bug report to the bug tracker:
|
|http://sourceforge.net/bugs/?group_id=5470

Nice test. I thought maybe __pow__ might be different in not having a
reverse form, but indeed, int has an __rpow__ method.

Before submitting, make sure that this does not work in 2.5, and then say
so in the bug report. In fact, copy the version/system info that the
interactive interpreter puts up when it starts.

FWIW:
Python 2.5 (r25:51908, Jan 21 2007, 03:10:25)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-3)] on HOSTNAME_REDACTED
Type "help", "copyright", "credits" or "license" for more information..... __pow__ = int.__add__
....74088
 
L

Lenard Lindstrom

looping said:
Hi,
for the fun I try operator overloading experiences and I didn't
exactly understand how it works.

Here is my try:
def __pow__(self, value):
return self.__add__(value)

6

OK, it works. Now I try different way to achieve the same result but
without much luck:

__pow__ = int.__add__

or:

but for every try the result was the same:
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...... pass
.....

Lenard Lindstrom
 
S

Steve Holden

| This looks like a bug in Python. It works for all the other
| operators: [SNIP]
| >>> i ** 3
| 74088
|
| You should submit a bug report to the bug tracker:
|
|http://sourceforge.net/bugs/?group_id=5470

Nice test. I thought maybe __pow__ might be different in not having a
reverse form, but indeed, int has an __rpow__ method.

Before submitting, make sure that this does not work in 2.5, and then say
so in the bug report. In fact, copy the version/system info that the
interactive interpreter puts up when it starts.

FWIW:
Python 2.5 (r25:51908, Jan 21 2007, 03:10:25)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-3)] on HOSTNAME_REDACTED
Type "help", "copyright", "credits" or "license" for more information.... __pow__ = int.__add__
...74088
Interestingly, Jython doesn't suffer from this drawback, which I suspect
is due to special-casing of the __pow__ operator that was discussed
quite recently on python-dev without anybody noticing this aspect of things.

C:\jython2.2b1>jython
[...].... pass
....
Note that it still doesn't work to override the *instance's* method.

regards
Steve
 
S

Steve Holden

| This looks like a bug in Python. It works for all the other
| operators: [SNIP]
| >>> i ** 3
| 74088
|
| You should submit a bug report to the bug tracker:
|
|http://sourceforge.net/bugs/?group_id=5470

Nice test. I thought maybe __pow__ might be different in not having a
reverse form, but indeed, int has an __rpow__ method.

Before submitting, make sure that this does not work in 2.5, and then say
so in the bug report. In fact, copy the version/system info that the
interactive interpreter puts up when it starts.

FWIW:
Python 2.5 (r25:51908, Jan 21 2007, 03:10:25)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-3)] on HOSTNAME_REDACTED
Type "help", "copyright", "credits" or "license" for more information.... __pow__ = int.__add__
...74088
Interestingly, Jython doesn't suffer from this drawback, which I suspect
is due to special-casing of the __pow__ operator that was discussed
quite recently on python-dev without anybody noticing this aspect of things.

C:\jython2.2b1>jython
[...].... pass
....
Note that it still doesn't work to override the *instance's* method.

regards
Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,110
Latest member
OdetteGabb
Top