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