Automatically generating arithmetic operations for a subclass

S

Steven D'Aprano

I have a subclass of int where I want all the standard arithmetic
operators to return my subclass, but with no other differences:

class MyInt(int):
def __add__(self, other):
return self.__class__(super(MyInt, self).__add__(other))
# and so on for __mul__, __sub__, etc.


My quick-and-dirty count of the __magic__ methods that need to be over-
ridden comes to about 30. That's a fair chunk of unexciting boilerplate.

Is there a trick or Pythonic idiom to make arithmetic operations on a
class return the same type, without having to manually specify each
method? I'm using Python 2.5, so anything related to ABCs are not an
option.

Does anyone have any suggestions?
 
P

Paul McGuire

I have a subclass of int where I want all the standard arithmetic
operators to return my subclass, but with no other differences:

class MyInt(int):
    def __add__(self, other):
        return self.__class__(super(MyInt, self).__add__(other))
    # and so on for __mul__, __sub__, etc.

My quick-and-dirty count of the __magic__ methods that need to be over-
ridden comes to about 30. That's a fair chunk of unexciting boilerplate.

Something like this maybe?

def takesOneArg(fn):
try:
fn(1)
except TypeError:
return False
else:
return True

class MyInt(int): pass

template = "MyInt.__%s__ = lambda self, other: self.__class__(super
(MyInt, self).__%s__(other))"
fns = [fn for fn in dir(int) if fn.startswith('__') and takesOneArg
(getattr(1,fn))]
print fns
for fn in fns:
exec(template % (fn,fn))


Little harm in this usage of exec, since it is your own code that you
are running.

-- Paul
 
A

Arnaud Delobelle

Steven D'Aprano said:
I have a subclass of int where I want all the standard arithmetic
operators to return my subclass, but with no other differences:

class MyInt(int):
def __add__(self, other):
return self.__class__(super(MyInt, self).__add__(other))
# and so on for __mul__, __sub__, etc.


My quick-and-dirty count of the __magic__ methods that need to be over-
ridden comes to about 30. That's a fair chunk of unexciting boilerplate.

Is there a trick or Pythonic idiom to make arithmetic operations on a
class return the same type, without having to manually specify each
method? I'm using Python 2.5, so anything related to ABCs are not an
option.

Does anyone have any suggestions?

I do this:

binops = ['add', 'sub', 'mul', 'div', 'radd', 'rsub'] # etc
unops = ['neg', 'abs', invert'] # etc

binop_meth = """
def __%s__(self, other):
return type(self)(int.__%s__(self, other))
"""

unop_meth = """
def __%s__(self):
return type(self)(int.__%s__(self))
"""

class MyInt(int):
for op in binops:
exec binop_meth % (op, op)
for op in unops:
exec unop_meth % (op, op)
del op

HTH
 
A

Arnaud Delobelle

Arnaud Delobelle said:
binops = ['add', 'sub', 'mul', 'div', 'radd', 'rsub'] # etc
unops = ['neg', 'abs', invert'] # etc

Oops. There's a missing quote above. It should have been, of course:

unops = ['neg', 'abs', 'invert'] # etc
 
G

Gerard Flanagan

Steven said:
I have a subclass of int where I want all the standard arithmetic
operators to return my subclass, but with no other differences:

class MyInt(int):
def __add__(self, other):
return self.__class__(super(MyInt, self).__add__(other))
# and so on for __mul__, __sub__, etc.

Just an idea:


def myint(meth):
def mymeth(*args):
return MyInt(meth(*args))
return mymeth

class MyIntMeta(type):

method_names = 'add sub mul neg'.split()

def __new__(cls, name, bases, attrs):
t = type.__new__(cls, name, bases, attrs)
for name in MyIntMeta.method_names:
name = '__%s__' % name
meth = getattr(t, name)
setattr(t, name, myint(meth))
return t


class MyInt(int):
__metaclass__ = MyIntMeta

a = MyInt(3)
b = MyInt(3000)

print a
print b
c = a + b
print c
assert isinstance(c, MyInt)
d = c * MyInt(4)
print d
e = c * 6 * a * b
print e
assert isinstance(e, MyInt)

x = -e
print x
assert isinstance(x, MyInt)
 
D

Diez B. Roggisch

andrew said:
Arnaud said:
I do this:

binops = ['add', 'sub', 'mul', 'div', 'radd', 'rsub'] # etc
unops = ['neg', 'abs', invert'] # etc

binop_meth = """
def __%s__(self, other):
return type(self)(int.__%s__(self, other))
"""

unop_meth = """
def __%s__(self):
return type(self)(int.__%s__(self))
"""

class MyInt(int):
for op in binops:
exec binop_meth % (op, op)
for op in unops:
exec unop_meth % (op, op)
del op

what's the "del" for?

To not pollute the namespace of MyInt - otherwise, you could do

MyInt(10).op


Diez
 
A

Arnaud Delobelle

andrew cooke said:
Arnaud said:
I do this:

binops = ['add', 'sub', 'mul', 'div', 'radd', 'rsub'] # etc
unops = ['neg', 'abs', invert'] # etc

binop_meth = """
def __%s__(self, other):
return type(self)(int.__%s__(self, other))
"""

unop_meth = """
def __%s__(self):
return type(self)(int.__%s__(self))
"""

class MyInt(int):
for op in binops:
exec binop_meth % (op, op)
for op in unops:
exec unop_meth % (op, op)
del op

what's the "del" for?

Without it, 'op' would end up as a class attribute.
 
N

norseman

Steven said:
I have a subclass of int where I want all the standard arithmetic
operators to return my subclass, but with no other differences:

class MyInt(int):
def __add__(self, other):
return self.__class__(super(MyInt, self).__add__(other))
# and so on for __mul__, __sub__, etc.


My quick-and-dirty count of the __magic__ methods that need to be over-
ridden comes to about 30. That's a fair chunk of unexciting boilerplate.

Is there a trick or Pythonic idiom to make arithmetic operations on a
class return the same type, without having to manually specify each
method? I'm using Python 2.5, so anything related to ABCs are not an
option.

Does anyone have any suggestions?
==============================
Have you thought about making a generator?
The boiler plate is above. Change the necessary parts to VARS and place
in a loop to write them out. Input file would have the 30 +/- lines to
be substituted.
---
zparts contents:
MyInt, __add__, other
MyMul, __mul__, other
..
..
---
zgen contents:
while read ztyp,zop,zother
do
print (line one..
print (line two..
print "return self.__class__(super($ztyp, self).&zop(zother))
.
.
done
---

(syntax not necessarily correct, but you get the idea)
Run redirected to lib or program source.



Steve
 
S

Steven D'Aprano

Is there a trick or Pythonic idiom to make arithmetic operations on a
class return the same type, without having to manually specify each
method? I'm using Python 2.5, so anything related to ABCs are not an
option.

Does anyone have any suggestions?

Metaclasses can be used for this purpuse, see the example for a Roman
number type [1]

[1] http://paste.pocoo.org/show/97258/


That's an interesting solution. I like it.


Thanks to all who responded, I see that there's no best practice to get
what I want, so I'll do some experimentation.
 
M

Michael

While thinking about Steven D'Aprano's thread about automatically
generating arithmetic operations for a subclass, I stumbled upon
something confusing. Having defined the following class to do funky
addition,

class MyInt(int):
def __getattribute__(self, key):
if key == "__add__":
print("In __getattribute__('__add__')")
return lambda other: MyInt(int.__add__(self, other+100))
else:
return object.__getattribute__(self, key)

def __getattr__(self, key):
if key == "__add__":
print("In __getattr__('__add__')")
return lambda other: MyInt(int.__add__(self, other+100))
else:
return object.__getattr__(self, key)

I then do this:
In __getattribute__('__add__')
106
Why doesn't "a + 2" look up the __add__ attribute and use my lambda?
If I manually define __add__(self, other) then "a + 2" will of course
use that method.

Michael
 
M

Michael

Hmm... I meant to create a new thread, as does GMail when you edit the
subject. Pardon my Google-Groups newbieness. - Michael
 
P

Piet van Oostrum

Michael said:
M> While thinking about Steven D'Aprano's thread about automatically
M> generating arithmetic operations for a subclass, I stumbled upon
M> something confusing. Having defined the following class to do funky
M> addition,
M> class MyInt(int):
M> def __getattribute__(self, key):
M> if key == "__add__":
M> print("In __getattribute__('__add__')")
M> return lambda other: MyInt(int.__add__(self, other+100))
M> else:
M> return object.__getattribute__(self, key)
M> def __getattr__(self, key):
M> if key == "__add__":
M> print("In __getattr__('__add__')")
M> return lambda other: MyInt(int.__add__(self, other+100))
M> else:
M> return object.__getattr__(self, key)
M> I then do this:
M> In __getattribute__('__add__')
M> 106
M> Why doesn't "a + 2" look up the __add__ attribute and use my lambda?
M> If I manually define __add__(self, other) then "a + 2" will of course
M> use that method.

This has just been discussed in the thread "Overriding methods
per-object". In short: In newstyle classes these methods when invoked
implicitely, e.g by a+2, are only looked up in the class, and bypass
__getattribute__.

See http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes
 
T

Terry Reedy

Michael said:
While thinking about Steven D'Aprano's thread about automatically
generating arithmetic operations for a subclass, I stumbled upon
something confusing. Having defined the following class to do funky
addition,

class MyInt(int):
def __getattribute__(self, key):
if key == "__add__":
print("In __getattribute__('__add__')")
return lambda other: MyInt(int.__add__(self, other+100))
else:
return object.__getattribute__(self, key)

def __getattr__(self, key):
if key == "__add__":
print("In __getattr__('__add__')")
return lambda other: MyInt(int.__add__(self, other+100))
else:
return object.__getattr__(self, key)

I then do this:

In __getattribute__('__add__')
106
6

Why doesn't "a + 2" look up the __add__ attribute and use my lambda?

Answer 1: because it was not programmed that way ;-).

Answer 2: because __getattribute__/__getattr__ are for looking up
attributes of instances of the class, whereas special methods are
generally required to be attributes of the class. So *their* lookup
would use type(MyInt).__getxxx__. When that fails, the add code looks
for int.__radd__. (I *think* this your answer.)
> If I manually define __add__(self, other) then "a + 2" will of course
use that method.

because that is that type(MyInt).__getxxx__ will find.

tjr
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top