Nenhuma das respostas aqui fornece código para você trabalhar para ilustrar realmente por que isso acontece no Python. E é divertido olhar para uma abordagem mais profunda, então aqui vai.
A principal razão pela qual isso não funciona como o esperado é porque no Python, quando você escreve:
i += 1
não está fazendo o que você pensa que está fazendo. Inteiros são imutáveis. Isso pode ser visto quando você olha para o que o objeto realmente é no Python:
a = 0
print('ID of the first integer:', id(a))
a += 1
print('ID of the first integer +=1:', id(a))
A função id representa um valor único e constante para um objeto em sua vida útil. Conceitualmente, ele mapeia livremente para um endereço de memória em C / C ++. Executando o código acima:
ID of the first integer: 140444342529056
ID of the first integer +=1: 140444342529088
Isso significa que o primeiro a
não é mais o mesmo que o segundo a
, porque seus IDs são diferentes. Efetivamente, eles estão em diferentes locais da memória.
Com um objeto, no entanto, as coisas funcionam de maneira diferente. Substituí o +=
operador aqui:
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))
A execução disso resulta na seguinte saída:
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
Observe que o atributo id nesse caso é realmente o mesmo para as duas iterações, mesmo que o valor do objeto seja diferente (você também pode encontrar o id
valor int que o objeto mantém, o que mudaria conforme a mutação - porque números inteiros são imutáveis).
Compare isso com quando você executa o mesmo exercício com um objeto imutável:
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))
Isso gera:
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
Algumas coisas aqui para notar. Primeiro, no loop com o +=
, você não está mais adicionando ao objeto original. Nesse caso, como ints estão entre os tipos imutáveis do Python , o python usa um ID diferente. Também é interessante notar que o Python usa o mesmo subjacente id
para várias variáveis com o mesmo valor imutável:
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 tem vários tipos imutáveis, que causam o comportamento que você vê. Para todos os tipos mutáveis, sua expectativa está correta.
i
for imutável ou se você estiver executando uma operação sem mutação. Com uma lista aninhadafor i in a: a.append(1)
teria um comportamento diferente; Python não copia as listas aninhadas. No entanto, números inteiros são imutáveis e a adição retorna um novo objeto, ele não altera o antigo.