while utiliza terminar que para for ejemplos como ciclo bucle anidado python loops numpy operators

python - utiliza - ¿Cuál es la diferencia entre i=i+1 e i+= 1 en un ciclo ''for''?



for en python 3 (6)

Esta pregunta ya tiene una respuesta aquí:

Descubrí algo curioso hoy y me preguntaba si alguien podría arrojar algo de luz sobre cuál es la diferencia aquí.

import numpy as np A = np.arange(12).reshape(4,3) for a in A: a = a + 1 B = np.arange(12).reshape(4,3) for b in B: b += 1

Después de ejecutar cada bucle for , A no ha cambiado, pero B ha agregado uno a cada elemento. De hecho, uso la versión B para escribir en una matriz NumPy inicializada dentro de un bucle for .


Como ya se señaló, b += 1 actualiza b en el lugar, mientras que a = a + 1 calcula a + 1 y luego asigna el nombre a al resultado (ahora a ya no se refiere a una fila de A ).

Sin embargo, para comprender el operador += correctamente, también debemos comprender el concepto de objetos mutables versus inmutables . Considere lo que sucede cuando dejamos de lado la .reshape :

C = np.arange(12) for c in C: c += 1 print(C) # [ 0 1 2 3 4 5 6 7 8 9 10 11]

Vemos que C no se actualiza, lo que significa que c += 1 y c = c + 1 son equivalentes. Esto se debe a que ahora C es una matriz 1D ( C.ndim == 1 ) y, por lo tanto, al iterar sobre C , cada elemento entero se extrae y se asigna a c .

Ahora en Python, los enteros son inmutables, lo que significa que las actualizaciones en el lugar no están permitidas, transformando efectivamente c += 1 en c = c + 1 , donde c ahora se refiere a un nuevo entero, no acoplado a C de ninguna manera. Cuando realiza un bucle sobre las matrices reformadas, se asignan filas enteras ( np.ndarray ''s) a b (y a ) a la vez, que son objetos mutables , lo que significa que se le permite pegar nuevos enteros a voluntad, lo que sucede cuando haces a += 1 .

Cabe mencionar que, aunque + y += están relacionados como se describe anteriormente (y muy a menudo lo están), cualquier tipo puede implementarlos de la forma que desee definiendo los métodos __add__ y __iadd__ , respectivamente.


En el primer ejemplo, está reasignando la variable a , mientras que en el segundo está modificando los datos in situ, utilizando el operador += .

Vea la sección sobre 7.2.1. Declaraciones de asignación aumentadas :

Una expresión de asignación aumentada como x += 1 puede reescribirse como x = x + 1 para lograr un efecto similar, pero no exactamente igual. En la versión aumentada, x solo se evalúa una vez. Además, cuando sea posible, la operación real se realiza in situ , lo que significa que, en lugar de crear un nuevo objeto y asignarlo al objetivo, el objeto antiguo se modifica en su lugar.

+= operador llama a __iadd__ . Esta función realiza el cambio en el lugar, y solo después de su ejecución, el resultado se vuelve a establecer en el objeto que está "aplicando" el += activado.

__add__ por otro lado toma los parámetros y devuelve su suma (sin modificarlos).


En primer lugar: las variables ayb en los bucles se refieren a objetos numpy.ndarray .

En el primer bucle, a = a + 1 se evalúa de la siguiente manera: se __add__(self, other) la función __add__(self, other) de numpy.ndarray . Esto crea un nuevo objeto y, por lo tanto, A no se modifica. Luego, la variable a se establece para referirse al resultado.

En el segundo bucle, no se crea ningún objeto nuevo. La instrucción b += 1 llama a la función __iadd__(self, other) de numpy.ndarray que modifica el objeto ndarray en el lugar al que se refiere b. Por lo tanto, B se modifica.


La diferencia es que uno modifica la estructura de datos en sí (operación en el lugar) b += 1 mientras que el otro simplemente reasigna la variable a = a + 1 .

Solo para completar:

x += y no siempre está haciendo una operación en el lugar, hay (al menos) tres excepciones:

  • Si x no implementa un método __iadd__ , entonces la declaración x += y es solo una abreviatura de x = x + y . Este sería el caso si x fuera algo así como un int .

  • Si __iadd__ devuelve NotImplemented , Python vuelve a x = x + y .

  • El método __iadd__ podría implementarse teóricamente para no funcionar en su lugar. Sin embargo, sería realmente extraño hacer eso.

Como sucede, sus b s son numpy.ndarray s que implementa __iadd__ y se devuelve para que su segundo bucle modifique la matriz original en su lugar.

Puede leer más sobre esto en la documentación de Python de "Emulación de tipos numéricos" .

Estos __i*__ [ __i*__ ] están llamados a implementar las asignaciones aritméticas aumentadas ( += , -= , *= , @= , /= , //= , %= , **= , <<= , >>= , &= , ^= , |= ). Estos métodos deberían intentar realizar la operación en el lugar (modificando self) y devolver el resultado (que podría ser, pero no tiene que ser, self). Si no se define un método específico, la asignación aumentada recurre a los métodos normales. Por ejemplo, si x es una instancia de una clase con un __iadd__() , x += y es equivalente a x = x.__iadd__(y) . De lo contrario, se consideran x.__add__(y) e y.__radd__(x) , como con la evaluación de x + y . En ciertas situaciones, la asignación aumentada puede a_tuple[i] += ["item"] errores inesperados (consulte ¿Por qué a_tuple[i] += ["item"] genera una excepción cuando la suma funciona? ), Pero este comportamiento es, de hecho, parte del modelo de datos.


La forma corta ( a += 1 ) tiene la opción de modificar a lugar, en lugar de crear un nuevo objeto que represente la suma y volver a vincularlo con el mismo nombre ( a = a + 1 ). Entonces, la forma corta ( a += 1 ) es mucho más eficiente ya que no necesariamente necesita hacer una copia de a diferencia de a = a + 1 .

Además, incluso si generan el mismo resultado, observe que son diferentes porque son operadores separados: + y +=


Una cuestión clave aquí es que este bucle itera sobre las filas (primera dimensión) de B :

In [258]: B Out[258]: array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]]) In [259]: for b in B: ...: print(b,''=>'',end='''') ...: b += 1 ...: print(b) ...: [0 1 2] =>[1 2 3] [3 4 5] =>[4 5 6] [6 7 8] =>[7 8 9] [ 9 10 11] =>[10 11 12]

Por lo tanto, el += está actuando sobre un objeto mutable, una matriz.

Esto está implícito en las otras respuestas, pero se pierde fácilmente si su enfoque está en la reasignación a a = a+1 .

También podría hacer un cambio en el lugar a b con [:] indexación, o incluso algo más elegante, b[1:]=0 :

In [260]: for b in B: ...: print(b,''=>'',end='''') ...: b[:] = b * 2 [1 2 3] =>[2 4 6] [4 5 6] =>[ 8 10 12] [7 8 9] =>[14 16 18] [10 11 12] =>[20 22 24]

Por supuesto, con una matriz 2D como B generalmente no necesitamos iterar en las filas. Muchas operaciones que funcionan en un solo B también funcionan en todo. B += 1 , B[1:] = 0 , etc.