None of the answers here give you any code to work with to really illustrate why this happens in Python land. And this is fun to look at in a more deep approach so here goes.
The primary reason that this doesn't work as you expect is because in Python, when you write:
i += 1
it is not doing what you think it's doing. Integers are immutable. This can be seen when you look into what the object actually is in Python:
a = 0
print('ID of the first integer:', id(a))
a += 1
print('ID of the first integer +=1:', id(a))
The id function represents a unique and constant value for an object in it's lifetime. Conceptually, it maps loosely to a memory address in C/C++. Running the above code:
ID of the first integer: 140444342529056
ID of the first integer +=1: 140444342529088
This means the first a
is no longer the same as the second a
, because their id's are different. Effectively they are at different locations in memory.
With an object, however, things work differently. I have overwritten the +=
operator here:
class CustomInt:
def __iadd__(self, other):
# Override += 1 for this class
self.value = self.value + other.value
return self
def __init__(self, v):
self.value = v
ints = []
for i in range(5):
int = CustomInt(i)
print('ID={}, value={}'.format(id(int), i))
ints.append(int)
for i in ints:
i += CustomInt(i.value)
print("######")
for i in ints:
print('ID={}, value={}'.format(id(i), i.value))
Running this results in the following output:
ID=140444284275400, value=0
ID=140444284275120, value=1
ID=140444284275064, value=2
ID=140444284310752, value=3
ID=140444284310864, value=4
######
ID=140444284275400, value=0
ID=140444284275120, value=2
ID=140444284275064, value=4
ID=140444284310752, value=6
ID=140444284310864, value=8
Notice that the id attribute in this case is actually the same for both iterations, even though the value of the object is different (you could also find the id
of the int value the object holds, which would be changing as it is mutating - because integers are immutable).
Compare this to when you run the same exercise with an immutable object:
ints_primitives = []
for i in range(5):
int = i
ints_primitives.append(int)
print('ID={}, value={}'.format(id(int), i))
print("######")
for i in ints_primitives:
i += 1
print('ID={}, value={}'.format(id(int), i))
print("######")
for i in ints_primitives:
print('ID={}, value={}'.format(id(i), i))
This outputs:
ID=140023258889248, value=0
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
######
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
ID=140023258889408, value=5
######
ID=140023258889248, value=0
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
A few things here to notice. First, in the loop with the +=
, you are no longer adding to the original object. In this case, because ints are among the immutable types in Python, python uses a different id. Also interesting to note that Python uses the same underlying id
for multiple variables with the same immutable value:
a = 1999
b = 1999
c = 1999
print('id a:', id(a))
print('id b:', id(b))
print('id c:', id(c))
id a: 139846953372048
id b: 139846953372048
id c: 139846953372048
tl;dr - Python has a handful of immutable types, which cause the behavior you see. For all mutable types, your expectation is correct.
i
is immutable or you're carrying out a non-mutating operation. With a nested listfor i in a: a.append(1)
would have different behaviour; Python does not copy the nested lists. However integers are immutable and addition returns a new object, it doesn't change the old one.a=[1,2,3];a.forEach(i => i+=1);alert(a)
. Same in C#i = i + 1
to affecta
?