Reputation: 53
class Number(object):
def __init__(self):
super(Number, self).__init__()
self.data = 10
def __getattr__(self, name):
def _missing(*args, **kwargs):
method = getattr(self.data, name)
return method(args[0])
return _missing
a = Number()
b = Number()
print a.__add__(10) # this is ok!
print a + 10 # TypeError: "unsupported operand type(s) for +: 'Number' and 'int'"
print a + b # TypeError: "unsupported operand type(s) for +: 'Number' and 'Number'"
Question: What's the difference between "a.__add__(10)" and "a + 10", How can I hook the operator "+" ?
Upvotes: 5
Views: 4573
Reputation: 4884
You can try this, but it's not beautiful:
import numbers
def redirect(name):
def redirected(self, *args):
assert len(args) <= 1
if len(args) == 1 and isinstance(args[0], Number):
return getattr(self._data, name)(args[0]._data)
else:
return getattr(self._data, name)(*args)
return redirected
names = set(['__gt__', '__ge__'])
names.update(numbers.Real.__abstractmethods__)
methods = dict((name, redirect(name)) for name in names)
methods.update(
{'__init__': lambda self, data: setattr(self, '_data', float(data))})
Number = type("Number", (), methods)
You can use it like this:
>>> a = Number(5)
>>> b = Number(7)
>>> a + 10
15.0
>>> a + b
12.0
>>> a > b
False
>>> a == Number(5.0)
True
Note that the type returned from the arithmetic operators is float
and not Number
(which may or may not be what you expected).
If you want to have integers, you can remove the float()
call and change numbers.Real
to numbers.Integral
.
Note, however, that the arithmetic operators will cease to work with float values.
Upvotes: 1
Reputation: 318508
Python will only use an actual __add__
method, not one that only "exists" due to __getattr__
.
When adding __add__ = (10).__add__
it works fine.
So what you'll want to do is adding proxy methods:
def __add__(self, *args): return self.data.__add__(*args)
def __sub__(self, *args): return self.data.__sub__(*args)
# ...
Upvotes: 5