简体   繁体   中英

Overload all arithmetic operators in Python

Suppose I build a class that basically represents a number plus some fancy stuff. Instances of that class should behave like numbers in any arithmetic/mathematical operation.

I could overload all numeric operators in that class, but is there no shorter solution?

The class basically looks like:

class MyFancyNumber:
    def __init__(self, num, info):
        self.num = num # the actual number
        self.info = info # some more info, or other data
    def doFancyStuff(self):
        # does something fancy
    def __add__(self, other):
        return self.num + other # same pattern for all numeric functions

What about this?

class MyFancyNumber(int):
    def __new__(cls, num, info=None):
        return super(MyFancyNumber, cls).__new__(cls, num)
    def __init__(self, num, info=None):
        self.num = num
        self.info = info
>>> MyFancyNumber(5)
5
>>> MyFancyNumber(5) + 2
7
>>> MyFancyNumber(5) / 4
1
>>> MyFancyNumber(5) * 0.5
2.5
>>> MyFancyNumber(5) - 7
-2
>>> MyFancyNumber(5, 'info').info
'info'

I guess based on the above, you can figure out what you need.

I don't endorse this as being particularly idiomatic, but...

Assuming all of your function definitions behave identically, like "just invoke the base behavior of the self.num class and apply all the non-self arguments to it", then you can loop through all the function names you want to define, and create each one using setattr . Example:

class MyFancyNumber(object):
    def __init__(self, num, info):
        self.num = num
        self.info = info
    def __repr__(self):
        return "MyFancyNumber({}, {})".format(repr(self.num), repr(self.info))

def make_func(name):
    return lambda self, *args: MyFancyNumber(getattr(self.num, name)(*args), self.info)

for name in ["__add__", "__sub__", "__mul__", "__div__", "__invert__", "__neg__", "__pos__"]:
    setattr(MyFancyNumber, name, make_func(name))

x = MyFancyNumber(50, "hello")
print(x + 10)
print(x - 10)
print(x * 10)
print(x / 10)
print(~x)
print(-x)
print(+x)

Result:

MyFancyNumber(60, 'hello')
MyFancyNumber(40, 'hello')
MyFancyNumber(500, 'hello')
MyFancyNumber(5, 'hello')
MyFancyNumber(-51, 'hello')
MyFancyNumber(-50, 'hello')
MyFancyNumber(50, 'hello')

Edit: I wasn't sure whether you wanted the result of arithmetic to be a MyFancyNumber or a regular built-in numerical type, but either way, the implementation is pretty similar:

class MyFancyNumber(object):
    def __init__(self, num, info):
        self.num = num
        self.info = info
    def __repr__(self):
        return "MyFancyNumber({}, {})".format(repr(self.num), repr(self.info))

def make_func(name):
    return lambda self, *args: getattr(self.num, name)(*args)

for name in ["__add__", "__sub__", "__mul__", "__div__", "__invert__", "__neg__", "__pos__"]:
    setattr(MyFancyNumber, name, make_func(name))

x = MyFancyNumber(50, "hello")
print(x + 10)
print(x - 10)
print(x * 10)
print(x / 10)
print(~x)
print(-x)
print(+x)

Result:

60
40
500
5
-51
-50
50

No, you have to define all the arithmetic operators otherwise how would Python know what to do with them. Don't forget you also need the reverse operators like __radd__ .

Also the code you've written returns an int for x+1 . Did you mean that or did you want adding a fancy number to return another fancy number?

You could just subclass int or float . Then you don't have to reimplement the operators but you'll still lose the special nature whenever you operate on a value.

A better solution would just be to have the numeric value in an attribute and convert to numbers explicitly when that's what you want. You can use __int__() and __float__() to implement the conversion.

The documentation covers what you need to do if you really do want to emulate a numeric type: for Python 3.x https://docs.python.org/3/reference/datamodel.html?highlight= int #emulating-numeric-types or for Python 2.x https://docs.python.org/2/reference/datamodel.html?highlight= int #emulating-numeric-types

This works for me in python 2.7 as long as you pass just one argument in the init . Sadly have no idea why it works though.

class MyFancyNumber(int):
    def __init__(self, num):
        self.num = num # the actual number

    def add_info(self,info):
        self.info = info  ## Add the info separately

    def doFancyStuff(self):
        # does something fancy

print MyFancyNumber(5)+5    

Usage

f = MyFancyNumber(2)
f.add_info(info)
f+4               ## returns 6

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM