I have a class foo that is essentially a float with some extra attributes attached. I can overwrite its __sub__ method so that I can do subtraction one direction, but I can't figure out how to do it the other way:
class foo():
def __init__(self, value, otherstuff):
self.value = value
self.otherstuff = otherstuff
def __sub__(self, other):
return self.value - other
a = 5
b = foo(12, 'blue')
print b-a # this works fine and returns 7
print a-b # I want this to return -7 but it obviously doesn't work
Is there a way to do this?
A general solution for add, sub, mul, div would be ideal, but sub and div are most pressing since they're not reversible.
You just need to override __rsub__, for right-hand side subtraction:
class foo():
def __init__(self, value, otherstuff):
self.value = value
self.otherstuff = otherstuff
def __sub__(self, other):
return self.value - other
def __rsub__(self, other):
return other - self.value
Output:
print(b - a)
7
print(a - b)
-7
There are similar methods like __radd__, __rmul__ for other operations.
Related
Basically my Question: Is there an elegant way, if I have a given Container class
class Container:
def __init__(self, value):
self.value = value
to pass any operator to the value parameter of the Container class?
So one obvious solution would be to override any single operator on its own. So for example for + I could do:
class Container:
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.value + other
so that for example
c1 = Container(1)
c1 += 1
print(c1)
would result in
2
and
c2 = Container("ab")
c2 += "c"
print(c2)
would result in
abc
So what I could do is to override all arithmetic built-in functions from python. But my Question is, is there a more elegant (shorter) way to do this for any operator?
You can factor out some of the more repetitive boilerplate:
import operator
class Container:
def __init__(self, value):
self.value = value
def _generic_op(f):
def _(self, other):
return f(self.value, other)
return _
__add__ = _generic_op(operator.add)
__sub__ = _generic_op(operator.sub)
__mul__ = _generic_op(operator.mul)
# etc
# No need for _generic_op as a class attribute
del _generic_op
If the number of overloads are smaller than the different classes you want to overload in, you can define saperate class for each overload and inherit it. So,
class Adder:
def __add__(self, val):
self.value += val
return self
class Subtractor:
def __sub__(self, val):
self.value -= val
return self
class Foo(Adder, Subtractor):
def __init__(self, value):
self.value = value
foo = Foo(5)
foo -= 3
foo += 7
print(foo.value)
But I am against doing such operations between custom DataTypes and native dataTypes. Consider this just a simple example that doesn't follow good coding principles in that sense.
If incase you still want to allow adding int/float directly, you might also want to handle Foo class objects inside too.
In Python, data types (like int, float) both represent a value, but also have some built-in attributes/functions/etc:
In [1]: a = 1.2
In [2]: a
Out[2]: 1.2
In [3]: a.is_integer()
Out[3]: False
Is it possible to reproduce this behavior within Python, e.g. define a class:
class Scalar:
def __init__(self, value)
self.value = value
# other code ....
s = Scalar(1.2)
where I could have s return 1.2 (instead of typing s.value), and do things like a = s -> a = 1.2? The closest I can get to this behavior is adding something like:
def __getitem__(self, key=None):
return self.value
and using a = s[()], but that doesn't look very good.
where I could have s return 1.2 (instead of typing s.value)
In the console? Then implement the __repr__ method.
a = s -> a = 1.2
To avoid having to use a = s.value, you can implement __call__ and call the object:
>>> class Scalar:
... def __init__(self, value):
... self.value = value
... def __repr__(self):
... return str(self.value)
... def __call__(self):
... return self.value
...
>>> s = Scalar(1.2)
>>> s
1.2
>>> a = s()
>>> a
1.2
Check the documentation about the data model on emulating numeric types.
For example:
class Scalar:
def __init__(self, value):
self.value = value
def __repr__(self):
return str(self.value)
def __call__(self):
return self.value
def __add__(self, other):
return Scalar(self.value + other.value)
def __lt__(self, other):
return self.value < other.value
def ___le__(self, other):
return self.value <= other.value
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return self.value != other.value
def __gt__(self, other):
return self.value > other.value
def __ge__(self, other):
return self.value >= other.value
Can be used like this:
>>> s1 = Scalar(1.2)
>>> s2 = Scalar(2.1)
>>> s1 + s2
3.3
>>> s1 < s2
True
>>> s1 > s2
False
>>> s1 != s2
True
>>> s1 <= s2
True
>>> s1 >= s2
False
There are also the __int__ and __float__ magic methods, which you can implement and use like this (this is more semantically correct):
>>> a = int(s)
>>> a = float(s)
As far as I know, that's not possible for your a = s example. You would have to change the behavior of =, the assignment operator. The assignment operator doesn't really do anything to the object on the right, it just copies a reference to it (in the case of an object, at least).
In general, it is possible to change the behavior of built in operators for your custom classes using operator overloading, but Python doesn't provide this sort of option for assignment (=) because of how different it is from operators like addition (+) and even equality (==).
I've run into some confusing behaviour of the magic comparison methods.
Suppose we have the following class:
class MutNum(object):
def __init__ (self, val):
self.val = val
def setVal(self, newval):
self.val = newval
def __str__(self):
return str(self.val)
def __repr__(self):
return str(self.val)
# methods for comparison with a regular int or float:
def __eq__(self, other):
return self.val == other
def __gt__(self, other):
return self.val > other
def __lt__(self, other):
return self.val < other
def __ge__(self, other):
return self.__gt__(other) or self.__eq__(other)
def __le__(self, other):
return self.__lt__(other) or self.__eq__(other)
The class does what it is supposed to do, comparing a MutNum object to a regular int or float is no problem. However, and this is what I don't understand, it even compares fine when the magic methods are given two MutNum objects.
a = MutNum(42)
b = MutNum(3)
print(a > b) # True
print(a >= b) # True
print(a < b) # False
print(a <= b) # False
print(a == b) # False
Why does this work? Thanks.
It evaluates as follows (using a repr-like notation instead of referring to variables):
MutNum(42) > MutNum(3)
=> MutNum(42).__gt__(MutNum(3))
=> MutNum(42).val > MutNum(3)
=> 42 > MutNum(3)
And from there, it's just the int-MutNum comparision you already know works.
If you throw in some print's and/or sys.stderr.write's, I think you'll see what's happening. EG:
def __gt__(self, other):
sys.stderr.write('__gt__\n')
sys.stderr.write('{}\n'.format(type(other)))
sys.stderr.write('{} {}\n'.format(self.val, other))
result = self.val > other
sys.stderr.write('result {}\n'.format(result))
return result
def __lt__(self, other):
sys.stderr.write('__lt__\n')
sys.stderr.write('{}\n'.format(type(other)))
sys.stderr.write('{} {}\n'.format(self.val, other))
result = self.val < other
sys.stderr.write('result {}\n'.format(result))
return result
When you try to compare self.val (an int) to other (a MutNum), python realizes it has nothing for comparing an int to a MutNum, and reverses the order of the comparison, and compares a MutNum to an int - which is something you've defined. That is, a single > comparison is doing the > as you'd expect, but it's also doing a <.
Let's say we have a class:
NOTE: this is a dummy class only.
class C(object):
def __init__(self):
self.a = -10
self.b = 5
self.c = 2
def modify(self, **kwargs):
for keyword in kwargs:
vars(self)[keyword] = kwargs[keyword]
return(self)
And we want to use this modify method to change values in our object:
myclass = C()
myclass = myclass.modify(a=10)
But when I want to change the value based on the original one, I have to write this:
myclass = C()
myclass = myclass.modify(a=myclass.a/10)
Or:
myclass = myclass.modify(a=abs(myclass.a))
My question is, is there a way, to create a global variable in a module, that I can import and use it as a placeholder for current value, so I can use this formula:
from globvars import current
myclass = C()
myclass = myclass.modify(
a=abs(current) % current ** 2,
b=current//2,
c=bool(current)
)
First I tried to a create a class, which will store the operation it is taking and a value, and modify() will look first for its variable as a keyword and then execute the function. Actually it is only working for simple situations like: current+10 or current**2.
But when I realised, I want to use this current for example with an hsba(current) (color converter) function, where current is pointing to an object stored in an other object, I just give up, I can't write this to every class I'm going to use..
Is there a solution for this? Maybe it's quite easy, I just can't see it :)
Thanks in advance for replies!
Here is a working solution. It is not complete and full of pretty bad design choices, but I hope it helps.
class Expr(object):
def __init__(self, op, left, right):
self.op = op
self.left = left
self.right = right
def __call__(self, current):
l = self._replace_current(self.left, current)
r = self._replace_current(self.right, current)
return self._do_operation(l, r)
def _replace_current(self, val, current):
if val == 'current':
return current
elif isinstance(val, Expr): # recurse
return val(current)
else:
return val
def _do_operation(self, l, r):
if self.op == '+':
return l + r
elif self.op == '*':
return l * r
elif self.op == '-':
return l - r
def __add__(self, other):
return self._left_op('+', other)
def __radd__(self, other):
return self._right_op('+', other)
def __mul__(self, other):
return self._left_op('*', other)
def __rmul__(self, other):
return self._right_op('*', other)
def __sub__(self, other):
return self._left_op('-', other)
def __rsub__(self, other):
return self._right_op('-', other)
def _left_op(self, op, other):
if isinstance(other, Current):
return Expr(op=op, left=self, right='current')
else:
return Expr(op=op, left=self, right=other)
def _right_op(self, op, other):
if isinstance(other, Current):
return Expr(op=op, left='current', right=self)
else:
return Expr(op=op, left=other, right=self)
class Current(Expr):
def __init__(self):
super(Current, self).__init__(None, None, None)
def __call__(self, current):
return current
def _left_op(self, op, other):
return Expr(op=op, left='current', right=other)
def _right_op(self, op, other):
return Expr(op=op, left=other, right='current')
current = Current()
class YourObj(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __call__(self, **kw):
for key, val in kw.iteritems():
# You should probably make sure it is actually an attribute of YourObj
if isinstance(val, Expr):
current = self.a
new_val = val(current)
setattr(self, key, new_val)
else:
setattr(self, key, val)
And you can do something like:
obj = YourObj(a=4, b=5)
obj(a=current - 4 + current * current)
This is basically an expression interpreter embedded in python's math operations.
Whenever you use an operation on current (like +), it will return an Expr (because it overrides __add__ and __radd__) that will register which operation this is, and what are each of its operands. These expressions can be nested, so if you say current + 4 - current, it will return Expr(op='-', left=Expr(op='+', left='current', right=4), right='current').
An expression can then be evaluated by calling it like a function and passing it the value that should replace 'current'. When you evaluate an expression, it will:
replace all the occurences of 'current' by the value passed
recursively evaluate the nested functions
return the end result of the whole expression
When you do obj(a=current + 4), the __call__ method of YourObj is called. It will evaluate the expression resulting of current + 4 and store it in a.
I hope this is clearer. Maybe I should rename some of the 'current' to make it less confusing.
Your modify method could take the name of the attribute to modify, and a function that takes the current value of the attribute and returns its new computed value. Then you can do something like:
def compute_new_value(current):
new_value = abs(current) % current ** 2
return new_value
myclass = C()
myclass.modify('a', compute_new_value)
For simple cases, lambda makes it less verbose:
myclass.modify('a', lambda cur: cur + 4)
And your class:
class C(object):
[...]
def modify(self, attr_name, func):
cur_value = getattr(self, attr_name)
new_value = func(cur_value)
setattr(self, attr_name, new_value)
Edit: I may have missed something. Since you're writing myclass = myclass.modify..., should the modify method return a copy of the object ?
You have a poor design, in my opinion, but you could do this using eval(). Of course, that just makes your design smell even more. Still...
class C(object):
# ...
def modify(self, **kwargs):
for name, expr in kwargs.iteritems():
setattr(self, name, eval(expr, vars(self)))
obj = C()
obj.modify(a="a+2", b="b*42")
The downside is that you have to pass the expressions as strings. Also, with this simple implementation, you can only use values defined on the instance in the expression (e.g., you cant access class attributes, or any attributes of parent classes, or globals). You could add the ability to use class attributes or globals and even parent classes by building the v dictionary in the appropriate order, of course:
def modify(self, **kwargs):
vardict = {} # allow globals and self attributes to be used in expressions
vardict.update(globals())
vardict.update(vars(self))
for name, expr in kwargs.iteritems():
value = eval(expr, v)
setattr(self, name, eval(expr, vardict))
vardict[name] = value
If you want a current variable that holds the current value, you could use this (inside the loop, since it needs to change for each attribute processed):
v["current"] = getattr(self, name, None)
One of the biggest drawbacks here is that you can't easily access variables from the caller's scope, although you could dig them out of the stack frame I guess... ugh. Or make the caller interpolate those into the string... double ugh.
Morphyn's answer is the proper way to do it, in my opinion. A lambda is hardly complicated
This was my old solution.. (sort of, this a dummy version of it)
class __current__(object):
def do(self, e, v = None):
c = __current__()
c.exp = e
if v is not None:
c.val = v
return(c)
def __abs__(self):
return(self.do(abs))
def __rpow__(self, v):
return(self.do(pow, v))
current = __current__()
class C(object):
def __call__(self, **kwargs):
for keyword, value in kwargs.iteritems():
try:
expression = value.exp
try:
value = expression(vars(self)[keyword], value.val)
except AttributeError:
value = expression(vars(self)[keyword])
except AttributeError:
value = value
setattr(self, keyword, value)
And the usage:
MyObj = C()
MyObj(a = -2)
MyObj(a = abs(current))
MyObj(a = 2 ** current)
In NumPy, it is possible to use the __array_priority__ attribute to take control of binary operators acting on an ndarray and a user-defined type. For instance:
class Foo(object):
def __radd__(self, lhs): return 0
__array_priority__ = 100
a = np.random.random((100,100))
b = Foo()
a + b # calls b.__radd__(a) -> 0
The same thing, however, doesn't appear to work for comparison operators. For instance, if I add the following line to Foo, then it is never called from the expression a < b:
def __rlt__(self, lhs): return 0
I realize that __rlt__ is not really a Python special name, but I thought it might work. I tried all of __lt__, __le__, __eq__, __ne__, __ge__, __gt__ with and without a preceding r, plus __cmp__, too, but I could never get NumPy to call any of them.
Can these comparisons be overridden?
UPDATE
To avoid confusion, here is a longer description NumPy's behavior. For starters, here's what the Guide to NumPy book says:
If the ufunc has 2 inputs and 1 output and the second input is an Object array
then a special-case check is performed so that NotImplemented is returned if the
second input is not an ndarray, has the array priority attribute, and has an
r<op> special method.
I think this is the rule that makes + work. Here's an example:
import numpy as np
a = np.random.random((2,2))
class Bar0(object):
def __add__(self, rhs): return 0
def __radd__(self, rhs): return 1
b = Bar0()
print a + b # Calls __radd__ four times, returns an array
# [[1 1]
# [1 1]]
class Bar1(object):
def __add__(self, rhs): return 0
def __radd__(self, rhs): return 1
__array_priority__ = 100
b = Bar1()
print a + b # Calls __radd__ once, returns 1
# 1
As you can see, without __array_priority__, NumPy interprets the user-defined object as a scalar type, and applies the operation at every position in the array. That's not what I want. My type is array-like (but should not be derived from ndarray).
Here's a longer example showing how this fails when all of the comparison methods are defined:
class Foo(object):
def __cmp__(self, rhs): return 0
def __lt__(self, rhs): return 1
def __le__(self, rhs): return 2
def __eq__(self, rhs): return 3
def __ne__(self, rhs): return 4
def __gt__(self, rhs): return 5
def __ge__(self, rhs): return 6
__array_priority__ = 100
b = Foo()
print a < b # Calls __cmp__ four times, returns an array
# [[False False]
# [False False]]
It looks like I can answer this myself. np.set_numeric_ops can be used as follows:
class Foo(object):
def __lt__(self, rhs): return 0
def __le__(self, rhs): return 1
def __eq__(self, rhs): return 2
def __ne__(self, rhs): return 3
def __gt__(self, rhs): return 4
def __ge__(self, rhs): return 5
__array_priority__ = 100
def override(name):
def ufunc(x,y):
if isinstance(y,Foo): return NotImplemented
return np.getattr(name)(x,y)
return ufunc
np.set_numeric_ops(
** {
ufunc : override(ufunc) for ufunc in (
"less", "less_equal", "equal", "not_equal", "greater_equal"
, "greater"
)
}
)
a = np.random.random((2,2))
b = Foo()
print a < b
# 4
I cannot reproduce your problem. The correct approach is to use __cmp__ special-method. If I write
import numpy as np
class Foo(object):
def __radd__(self, lhs):
return 0
def __cmp__(self, this):
return -1
__array_prioriy__ = 100
a = np.random.random((100,100))
b = Foo()
print a<b
and set a break point in the debugger, execution stops at the return -1.
Btw: __array_prioriy__ doesn't make any difference here: you have a typo in it!