Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Automatically generating arithmetic operations for a subclass

3 views
Skip to first unread message

Steven D'Aprano

unread,
Apr 14, 2009, 5:09:56 AM4/14/09
to
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?

--
Steven

Paul McGuire

unread,
Apr 14, 2009, 5:55:43 AM4/14/09
to
On Apr 14, 4:09 am, Steven D'Aprano

<ste...@REMOVE.THIS.cybersource.com.au> wrote:
> 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

Arnaud Delobelle

unread,
Apr 14, 2009, 6:16:04 AM4/14/09
to

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

--
Arnaud

Arnaud Delobelle

unread,
Apr 14, 2009, 6:20:52 AM4/14/09
to
Arnaud Delobelle <arn...@googlemail.com> writes:

> 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:

Gerard Flanagan

unread,
Apr 14, 2009, 7:36:57 AM4/14/09
to pytho...@python.org
Steven D'Aprano wrote:
> 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)


andrew cooke

unread,
Apr 14, 2009, 7:51:04 AM4/14/09
to Arnaud Delobelle, pytho...@python.org
Arnaud Delobelle wrote:
> 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?

curious,
andrew


Diez B. Roggisch

unread,
Apr 14, 2009, 8:14:11 AM4/14/09
to
andrew cooke wrote:

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

MyInt(10).op


Diez

Arnaud Delobelle

unread,
Apr 14, 2009, 9:08:47 AM4/14/09
to
"andrew cooke" <and...@acooke.org> writes:

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

> curious,
> andrew

--
Arnaud

andrew cooke

unread,
Apr 14, 2009, 10:32:30 AM4/14/09
to Arnaud Delobelle, pytho...@python.org

ah! ok, that makes sense, i guess. thanks.

(i just tried it out and you're right, of course, but also if binops and
unops are empty you get an error, although i guess that's no an issue
here).

andrew


Sebastian Wiesner

unread,
Apr 14, 2009, 1:24:56 PM4/14/09
to
<Steven D'Aprano – Dienstag, 14. April 2009 11:09>

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

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

--
Freedom is always the freedom of dissenters.
(Rosa Luxemburg)

norseman

unread,
Apr 14, 2009, 8:28:42 PM4/14/09
to Steven D'Aprano, pytho...@python.org
==============================
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

Steven D'Aprano

unread,
Apr 15, 2009, 5:17:44 AM4/15/09
to
On Tue, 14 Apr 2009 19:24:56 +0200, Sebastian Wiesner wrote:

>> 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.

--
Steven

Michael

unread,
Apr 18, 2009, 1:29:01 PM4/18/09
to
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:

>>> a = MyInt(4)
>>> a.__add__(2)
In __getattribute__('__add__')
106
>>> a + 2
6
>>>

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

Michael

unread,
Apr 18, 2009, 1:34:41 PM4/18/09
to
Hmm... I meant to create a new thread, as does GMail when you edit the
subject. Pardon my Google-Groups newbieness. - Michael

Piet van Oostrum

unread,
Apr 18, 2009, 4:31:30 PM4/18/09
to
>>>>> Michael <gund...@gmail.com> (M) wrote:

>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:

>>>>> a = MyInt(4)
>>>>> a.__add__(2)
>M> In __getattribute__('__add__')
>M> 106
>>>>> a + 2
>M> 6
>>>>>

>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
--
Piet van Oostrum <pi...@cs.uu.nl>
URL: http://pietvanoostrum.com [PGP 8DAE142BE17999C4]
Private email: pi...@vanoostrum.org

Terry Reedy

unread,
Apr 18, 2009, 4:33:01 PM4/18/09
to pytho...@python.org

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

0 new messages