Is it possible to override += in Python?
Yes, override the __iadd__ method. Example:
def __iadd__(self, other):
self.number += other.number
return self
In addition to what's correctly given in answers above, it is worth explicitly clarifying that when __iadd__ is overriden, the x += y operation does NOT end with the end of __iadd__ method.
Instead, it ends with x = x.__iadd__(y). In other words, Python assigns the return value of your __iadd__ implementation to the object you're "adding to", AFTER the implementation completes.
This means it is possible to mutate the left side of the x += y operation so that the final implicit step fails. Consider what can happen when you are adding to something that's within a list:
>>> x[1] += y # x has two items
Now, if your __iadd__ implementation (a method of an object at x[1]) erroneously or on purpose removes the first item (x[0]) from the beginning of the list, Python will then run your __iadd__ method) & try to assign its return value to x[1]. Which will no longer exist (it will be at x[0]), resulting in an ÌndexError.
Or, if your __iadd__ inserts something to beginning of x of the above example, your object will be at x[2], not x[1], and whatever was earlier at x[0] will now be at x[1]and be assigned the return value of the __iadd__ invocation.
Unless one understands what's happening, resulting bugs can be a nightmare to fix.
In addition to overloading __iadd__ (remember to return self!), you can also fallback on __add__, as x += y will work like x = x + y. (This is one of the pitfalls of the += operator.)
>>> class A(object):
... def __init__(self, x):
... self.x = x
... def __add__(self, other):
... return A(self.x + other.x)
>>> a = A(42)
>>> b = A(3)
>>> print a.x, b.x
42 3
>>> old_id = id(a)
>>> a += b
>>> print a.x
45
>>> print old_id == id(a)
False
It even trips up experts:
class Resource(object):
class_counter = 0
def __init__(self):
self.id = self.class_counter
self.class_counter += 1
x = Resource()
y = Resource()
What values do you expect x.id, y.id, and Resource.class_counter to have?
http://docs.python.org/reference/datamodel.html#emulating-numeric-types
For instance, to execute the statement
x += y, where x is an instance of a
class that has an __iadd__() method,
x.__iadd__(y) is called.
Related
NOTE: I am aware of this exact same question here and here. However, I have tried the solutions proposed by the answers there and they do not work for me (see sample code below).
A B object has a list of A. A is composed by a tuple of only two integers and an integer.
I am trying to use B objects as keys in a dictionary. However, even after implementing my own __eq__() and __hash__() methods, the length of my dictionary increases even after adding the same object to it.
See code below:
class A:
def __init__(self, my_tuple, my_integer):
self.my_tuple = my_tuple
self.my_integer = my_integer
def __eq__(self, other):
return self.my_tuple == other.my_tuple and self.my_integer == other.my_integer
class B:
def __init__(self):
self.list_of_A = []
def add(self, my_tuple, my_integer):
new_A = A(my_tuple, my_integer)
self.list_of_A.append(new_A)
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
for i in range(len(self.list_of_A)):
if self.list_of_A[i] != other.list_of_A[i]:
return False
return True
b_1 = B()
b_1.add((1,2), 3)
b_2 = B()
b_2.add((1,2), 3)
my_dict = {}
my_dict[b_1] = 'value'
print(len(my_dict))
my_dict[b_2] = 'value_2'
print(len(my_dict))
The output I am getting is
12
And the expected output is
11
Because I am adding the same object (i.e.:same properties values).
The hashes aren't equal because the repr()s aren't equal. Consider the following example I just did on my python console using your code:
>>> x = B()
>>> y = B()
>>> repr(x)
'<__main__.B object at 0x7f7b3a20c358>'
>>> repr(y)
'<__main__.B object at 0x7f7b3aa197b8>'
Obviously, x and y will have different hashes.
All you need to do, then, is overwrite __repr__() so that it outputs a deterministic value based on the contents of the object, rather than its memory address, and you should be good to go. In your case, that may look something like this:
class A:
...
def __repr__(self):
return f"A(my_tuple:{self.my_tuple}, my_integer:{self.my_integer})"
class B:
...
def __repr__(self):
return f"B(list_of_a:{self.list_of_a})"
How Python chooses which object to use the method overload?
For example:
class a:
def __init__(self, other):
self.data = other
def __add__(self, other):
return self.data + other
def __radd__(self,other):
return self.data + other
X = a(1)
X+1
1+X
Why in X + 1 expression , calls a method __add__ in object at the left, and in expression 1 + X method __add__ is called at object on the right?
X+1
first, calls:
X.__add__(1)
That succeeds, so no further work is needed.
On the other hand, this:
1+X
calls
(1).__add__(X)
That fails because int doesn't know how to interface with a class a. "As a last resort" this is tried instead:
X.__radd__(1)
From the docs on __radd__:
These functions are only called if the left operand does not support the corresponding operation and the operands are of different types.
I would like to subclass an immutable type or implement one of my own which behaves like an int does as shown in the following console session:
>>> i=42
>>> id(i)
10021708
>>> i.__iadd__(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__iadd__'
>>> i += 1
>>> i
43
>>> id(i)
10021696
Not surprisingly, int objects have no __iadd__() method, yet applying += to one doesn't result in an error, instead it apparently creates a new int and also somehow magically reassigns it to the name given in the augmented assignment statement.
Is it possible to create a user-defined class or subclass of a built-in immutable one that does this, and if so, how?
Simply don't implement __iadd__, but only __add__:
>>> class X(object):
... def __add__(self, o):
... return "added"
>>> x = X()
>>> x += 2
>>> x
'added'
If there's no x.__iadd__, Python simply calculates x += y as x = x + y doc.
The return value of __iadd__() is used. You don't need to return the object that's being added to; you can create a new one and return that instead. In fact, if the object is immutable, you have to.
import os.path
class Path(str):
def __iadd__(self, other):
return Path(os.path.join(str(self), str(other)))
path = Path("C:\\")
path += "windows"
print path
When it sees i += 1, Python will try to call __iadd__. If that fails, it'll try to call __add__.
In both cases, the result of the call will be bound to the name, i.e. it'll attempt i = i.__iadd__(1) and then i = i.__add__(1).
class aug_int:
def __init__(self, value):
self.value = value
def __iadd__(self, other):
self.value += other
return self
>>> i = aug_int(34)
>>> i
<__main__.aug_int instance at 0x02368E68>
>>> i.value
34
>>> i += 55
>>> i
<__main__.aug_int instance at 0x02368E68>
>>> i.value
89
>>>
Consider the following simple example class, which has a property that exposes a modified version of some internal data when called:
class Foo(object):
def __init__(self, value, offset=0):
self._value = value
self.offset = offset
#property
def value(self):
return self._value + self.offset
#value.setter
def value(self, value):
self._value = value
The value.setter works fine for regular assignment, but of course breaks down for compound assignment:
>>> x = Foo(3, 2)
>>> x.value
5
>>> x.value = 2
>>> x.value
4
>>> x.value += 5
>>> x.value
11
The desired behavior is that x.value += 5 should be equivalent to x.value = x._value + 5, as opposed to x.value = x.value + 5. Is there any way to achieve this (with the property interface) purely within the Foo class?
#ezod, there is no direct way to do what you're asking for, even with the descriptors protocol.
That kind behaviour of value property totally breaks the semantics of += operator.
Override the __iadd__ magic method.
You need to do that because += means "take the value and add five, then set that as the result". If you want it to know that the value isn't really the value, you need to change the semantics of the += operator.
I was confronted with the same problem and solved it with the following method:
class Foo(object):
def __init__(self):
self._y = 0
#property
def y(self):
return self._y + 5
#y.setter
def y(self, value):
value -= 5
self._y = value
>>> f = Foo()
>>> f.y
5
>>> f.y += 1
>>> f._y
1
>>> f.y
6
Could this be a solution? Have I overlooked something?
The desired behavior is that x.value += 5 should be equivalent to
x.value = x._value + 5
Why should Python know how you property is implemented (i.e. that setter assigns its value exactly to _x)?
Protocol of property gives you a possibility assign different actions (get, set, delete) to one name. And this protocol doesn't care about a way of implementation of these actions.
So it would be quite confusable if Python make some assumptions about your code and try to modify strait forward code logic.
A friend brought this to my attention, and after I pointed out an oddity, we're both confused.
Python's docs, say, and have said since at least 2.5.1 (haven't checked further back:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
Our confusion lies in the meaning of "y is evaluated only once".
Given a simple but contrived class:
class Magic(object):
def __init__(self, name, val):
self.name = name
self.val = val
def __lt__(self, other):
print("Magic: Called lt on {0}".format(self.name))
if self.val < other.val:
return True
else:
return False
def __le__(self, other):
print("Magic: Called le on {0}".format(self.name))
if self.val <= other.val:
return True
else:
return False
We can produce this result:
>>> x = Magic("x", 0)
>>> y = Magic("y", 5)
>>> z = Magic("z", 10)
>>>
>>> if x < y <= z:
... print ("More magic.")
...
Magic: Called lt on x
Magic: Called le on y
More magic.
>>>
This certainly looks like 'y' is, in a traditional sense "evaluated" twice -- once when x.__lt__(y) is called and performs a comparison on it, and once when y.__le__(z) is called.
So with this in mind, what exactly do the Python docs mean when they say "y is evaluated only once"?
The 'expression' y is evaluated once. I.e., in the following expression, the function is executed only one time.
>>> def five():
... print 'returning 5'
... return 5
...
>>> 1 < five() <= 5
returning 5
True
As opposed to:
>>> 1 < five() and five() <= 5
returning 5
returning 5
True
In the context of y being evaluated, y is meant as an arbitrary expression that could have side-effects. For instance:
class Foo(object):
#property
def complain(self):
print("Evaluated!")
return 2
f = Foo()
print(1 < f.complain < 3) # Prints evaluated once
print(1 < f.complain and f.complain < 3) # Prints evaluated twice