Ninguna de las respuestas aquí le brinda un código con el que trabajar para ilustrar realmente por qué sucede esto en Python land. Y esto es divertido de ver en un enfoque más profundo, así que aquí va.
La razón principal por la que esto no funciona como espera es porque en Python, cuando escribe:
i += 1
no está haciendo lo que crees que está haciendo. Los enteros son inmutables. Esto se puede ver cuando observa qué es realmente el objeto en Python:
a = 0
print('ID of the first integer:', id(a))
a += 1
print('ID of the first integer +=1:', id(a))
La función id representa un valor único y constante para un objeto en su vida útil. Conceptualmente, se asigna libremente a una dirección de memoria en C / C ++. Ejecutando el código anterior:
ID of the first integer: 140444342529056
ID of the first integer +=1: 140444342529088
Esto significa que el primero a
ya no es el mismo que el segundo.a
, porque sus identificaciones son diferentes. Efectivamente están en diferentes lugares en la memoria.
Con un objeto, sin embargo, las cosas funcionan de manera diferente. He sobrescrito el +=
operador aquí:
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))
Ejecutar esto da como resultado el siguiente resultado:
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
Tenga en cuenta que el atributo id en este caso es en realidad el mismo para ambas iteraciones, aunque el valor del objeto es diferente (también podría encontrar el id
valor int que tiene el objeto, que estaría cambiando a medida que muta, porque los enteros son inmutables)
Compare esto con cuando ejecuta el mismo ejercicio con un objeto inmutable:
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))
Esto produce:
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
Algunas cosas aquí para notar. Primero, en el ciclo con el +=
, ya no está agregando al objeto original. En este caso, dado que los ints se encuentran entre los tipos inmutables en Python , python usa una identificación diferente. También es interesante notar que Python usa el mismo subyacente id
para múltiples variables con el mismo valor inmutable:
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 tiene un puñado de tipos inmutables, que causan el comportamiento que ves. Para todos los tipos mutables, su expectativa es correcta.
i
es inmutable o si está llevando a cabo una operación no mutante. Con una lista anidadafor i in a: a.append(1)
tendría un comportamiento diferente; Python no copia las listas anidadas. Sin embargo, los enteros son inmutables y la suma devuelve un nuevo objeto, no cambia el anterior.