No tengo idea de cómo se llaman en realidad, pero los veo todo el tiempo. La implementación de Python es algo así como:
x += 5
como una notación abreviada para x = x + 5
.
Pero, ¿por qué se considera esto una buena práctica? Lo he encontrado en casi todos los libros o tutoriales de programación que he leído para Python, C, R , etc. Entiendo que es conveniente, ahorrando tres pulsaciones de teclas, incluidos los espacios. Pero siempre parecen hacerme tropezar cuando leo el código y, al menos en mi opinión, lo hacen menos legible, no más.
¿Me estoy perdiendo alguna razón clara y obvia por la que se usan en todo el lugar?
programming-practices
Fomite
fuente
fuente
x += 5
quex = x + 5
? ¿O es realmente solo azúcar sintáctico como sugieres?Respuestas:
No es taquigrafía.
El
+=
símbolo apareció en el lenguaje C en la década de 1970 y, con la idea C de "ensamblador inteligente", corresponde a una instrucción de máquina y un modo de dirección claramente diferentes:Cosas como "
i=i+1
","i+=1
"y"++i
", aunque en un nivel abstracto producen el mismo efecto, corresponden en un nivel bajo a una forma diferente de trabajar del procesador.En particular, esas tres expresiones, suponiendo que la
i
variable reside en la dirección de memoria almacenada en un registro de la CPU (D
por ejemplo, piense en ello como un "puntero a int") y la ALU del procesador toma un parámetro y devuelve un resultado en un "acumulador" (llamémoslo A - piense en ello como int).Con estas restricciones (muy comunes en todos los microprocesadores de ese período), la traducción probablemente será
La primera forma de hacerlo es desóptima, pero es más general cuando se opera con variables en lugar de constantes (
ADD A, B
oADD A, (D+x)
) o cuando se traducen expresiones más complejas (todas se reducen al presionar la operación de baja prioridad en una pila, llame a la alta prioridad, pop y repita hasta que todos los argumentos hayan sido eliminados).El segundo es más típico de la "máquina de estado": ya no estamos "evaluando una expresión", sino "operando un valor": todavía usamos la ALU, pero evitamos mover los valores como resultado permitido reemplazar el parámetro. Este tipo de instrucción no se puede usar donde se requieren expresiones más complicadas:
i = 3*i + i-2
no se puede operar en su lugar, ya quei
se requiere más veces.El tercero, incluso más simple, ni siquiera considera la idea de "suma", sino que usa un circuito más "primitivo" (en sentido computacional) para un contador. La instrucción se acorta, se carga más rápido y se ejecuta de inmediato, ya que la red combinatoria requerida para actualizar un registro para convertirlo en un contador es más pequeña y, por lo tanto, más rápida que la de un sumador completo.
Con los compiladores contemporáneos (consulte C, por ahora), que permiten la optimización del compilador, la correspondencia puede intercambiarse según la conveniencia, pero todavía hay una diferencia conceptual en la semántica.
x += 5
medioPero
x = x + 5
significa:Por supuesto, la optimización puede
&x
al acumuladorhaciendo que el código optimizado coincida con el
x += 5
uno.Pero esto solo se puede hacer si "encontrar x" no tiene efectos secundarios, de lo contrario
y
son semánticamente diferentes, ya que
x()
los efectos secundarios (admitirx()
es una función que hace cosas raras y devolver unint*
) se producirán dos o una vez.La equivalencia entre
x = x + y
y, porx += y
lo tanto, se debe al caso particular donde+=
y=
se aplican a un valor l directo.Para pasar a Python, heredó la sintaxis de C, pero como no hay traducción / optimización ANTES de la ejecución en lenguajes interpretados, las cosas no están necesariamente tan íntimamente relacionadas (ya que hay un paso de análisis menos). Sin embargo, un intérprete puede referirse a diferentes rutinas de ejecución para los tres tipos de expresión, aprovechando diferentes códigos de máquina dependiendo de cómo se forme la expresión y del contexto de evaluación.
Para quien le gusta más detalle ...
Cada CPU tiene una ALU (unidad aritmética-lógica) que es, en esencia, una red combinatoria cuyas entradas y salidas están "conectadas" a los registros y / o memoria dependiendo del código de operación de la instrucción.
Las operaciones binarias generalmente se implementan como "modificador de un registro de acumulador con una entrada tomada" en algún lugar ", donde en algún lugar puede estar, dentro del flujo de instrucciones en sí mismo (típico para el contenido manifiesto: ADD A 5), dentro de otro registro (típico para el cálculo de expresiones con temporales: por ejemplo, ADD AB): dentro de la memoria, en una dirección dada por un registro (típico de la obtención de datos, por ejemplo: ADD A (H)) - H, en este caso, funciona como un puntero de referencia.
Con este pseudocódigo,
x += 5
esmientras
x = x+5
esEs decir, x + 5 da un temporal que luego se asigna.
x += 5
opera directamente en x.La implementación real depende del conjunto de instrucciones reales del procesador: si no hay
ADD (.) c
código de operación, el primer código se convierte en el segundo: de ninguna manera.Si existe dicho código de operación, y la optimización está habilitada, la segunda expresión, después de eliminar los movimientos inversos y ajustar el código de operación de los registros, se convierte en la primera.
fuente
Dependiendo de cómo lo piense, en realidad es más fácil de entender porque es más sencillo. Tomar como ejemplo:
x = x + 5
invoca el procesamiento mental de "tomar x, agregarle cinco y luego asignar ese nuevo valor a x"x += 5
puede considerarse como "aumentar x en 5"Entonces, no es solo una taquigrafía, sino que describe la funcionalidad mucho más directamente. Cuando se leen fragmentos de código, es mucho más fácil de entender.
fuente
x = x + 5
todavía me preocupaba. Cuando me metí en las matemáticas a una edad posterior, me molestó aún más. El usox += 5
es significativamente más descriptivo y tiene mucho más sentido como expresión.reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1
... ¡oh, no! un error tipográficoAl menos en Python ,
x += y
yx = x + y
puede hacer cosas completamente diferentes.Por ejemplo, si lo hacemos
entonces
a += [3]
dará como resultadoa == b == [3]
, mientras quea = a + [3]
dará como resultadoa == [3]
yb == []
. Es decir,+=
modifica el objeto en el lugar (bueno, podría hacerlo, puede definir el__iadd__
método para hacer casi todo lo que quiera), mientras=
crea un nuevo objeto y le vincula la variable.Esto es muy importante cuando se realiza un trabajo numérico con NumPy , ya que con frecuencia termina con múltiples referencias a diferentes partes de una matriz, y es importante asegurarse de no modificar accidentalmente parte de una matriz a la que hay otras referencias, o copiar matrices innecesariamente (lo que puede ser muy costoso).
fuente
__iadd__
: hay lenguas donde se puede crear una referencia a una estructura de datos inmutables mutable, donde eloperator +=
se define, por ejemplo, Scala:val sb = StringBuffer(); lb += "mutable structure"
vsvar s = ""; s += "mutable variable"
. el más modifica el contenido de la estructura de datos mientras que el último hace que la variable apunte a una nueva.Se llama modismo . Los modismos de programación son útiles porque son una forma consistente de escribir una construcción de programación particular.
Cada vez que alguien escribe
x += y
, sabe quex
se está incrementandoy
y no por una operación más compleja (como práctica recomendada, por lo general, no mezclaría operaciones más complicadas y estas sintaxis shorthands). Esto tiene más sentido cuando se incrementa en 1.fuente
++x
yx+=1
son equivalentes en C y Java (tal vez también C #), aunque no necesariamente en C ++ debido a la compleja semántica del operador allí. La clave es que ambos evalúanx
una vez, incrementan la variable en uno y tienen un resultado que es el contenido de la variable después de la evaluación.++x + 3
que no es lo mismo quex += 1 + 3
. Paréntesisx += 1
y es idéntico. Como declaraciones en sí mismas, son idénticas.++x + 3
,x += 1 + 3
o(x += 1) + 3
tienen un comportamiento indefinido (suponiendo que el valor resultante "encaja").Para poner el punto de @ Pubby un poco más claro, considere
someObj.foo.bar.func(x, y, z).baz += 5
Sin el
+=
operador, hay dos formas de hacerlo:someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5
. Esto no solo es terriblemente redundante y largo, sino que también es más lento. Por lo tanto, uno tendría quetmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5
. Esto está bien, pero es mucho ruido por algo simple. En realidad, esto está muy cerca de lo que sucede en tiempo de ejecución, pero es tedioso escribir y solo usarlo+=
cambiará el trabajo al compilador / intérprete.La ventaja de
+=
otros operadores similares es innegable, mientras que acostumbrarse a ellos es solo cuestión de tiempo.fuente
Es cierto que es más corto y fácil, y es cierto que probablemente se inspiró en el lenguaje ensamblador subyacente, pero la mejor práctica es que evita toda una clase de errores y facilita la revisión del código y la seguridad Que hace.
Con
Como solo hay un nombre de variable involucrado, está seguro de lo que hace la declaración.
Con
RidiculouslyComplexName = RidiculosulyComplexName + 1;
Siempre hay dudas de que las dos partes son exactamente iguales. ¿Viste el error? Se pone aún peor cuando hay subíndices y calificadores presentes.
fuente
Si bien la notación + = es idiomática y más corta, estas no son las razones por las que es más fácil de leer. La parte más importante de la lectura del código es asignar la sintaxis al significado, por lo que cuanto más se aproxime la sintaxis a los procesos de pensamiento del programador, más legible será (esta es también la razón por la cual el código repetitivo es malo: no es parte del pensamiento proceso, pero aún es necesario para que el código funcione). En este caso, el pensamiento es "incrementar la variable x en 5", no "dejar que x sea el valor de x más 5".
Hay otros casos en los que una notación más corta es mala para la legibilidad, por ejemplo, cuando usa un operador ternario donde una
if
declaración sería más apropiada.fuente
Para tener una idea de por qué estos operadores están en los lenguajes 'C-style', hay un extracto de K&R 1st Edition (1978), hace 34 años:
Creo que está claro en este pasaje que Brian Kernighan y Dennis Ritchie (K&R) creían que los operadores de asignación compuesta ayudaron con la legibilidad del código.
Ha pasado mucho tiempo desde que K&R escribió eso, y muchas de las 'mejores prácticas' sobre cómo las personas deberían escribir código han cambiado o evolucionado desde entonces. Pero esta pregunta de programmers.stackexchange es la primera vez que recuerdo que alguien expresó una queja sobre la legibilidad de las tareas compuestas, por lo que me pregunto si muchos programadores consideran que son un problema. Por otra parte, mientras escribo esto, la pregunta tiene 95 votos a favor, por lo que tal vez las personas los encuentren discordantes al leer el código.
fuente
Además de la legibilidad, en realidad hacen cosas diferentes:
+=
no tiene que evaluar su operando izquierdo dos veces.Por ejemplo,
expr = expr + 5
se evaluaríaexpr
dos veces (suponiendo queexpr
sea impuro).fuente
expr = expr + 5
yexpr += 5
expr
tiene efectos secundarios.expr
tiene efectos secundarios,expr = expr + 5
debe invocar esos efectos secundarios dos veces.volatile
son efectos secundariosx=x+5
yx+=5
tienen los mismos efectos secundarios cuandox
esvolatile
Además de los méritos obvios que otras personas describieron muy bien, cuando tienes nombres muy largos es más compacto.
o
fuente
Es conciso.
Es mucho más corto de escribir. Implica menos operadores. Tiene menos superficie y menos posibilidades de confusión.
Utiliza un operador más específico.
Este es un ejemplo artificial, y no estoy seguro de si los compiladores reales implementan esto. x + = y en realidad usa un argumento y un operador y modifica x en su lugar. x = x + y podría tener una representación intermedia de x = z donde z es x + y. Este último utiliza dos operadores, suma y asignación, y una variable temporal. El operador único deja muy claro que el lado del valor no puede ser otra cosa que y y no necesita ser interpretado. Y, en teoría, podría haber alguna CPU elegante que tenga un operador más-igual que funcione más rápido que un operador más y un operador de asignación en serie.
fuente
ADD
instrucciones en muchas CPU tienen variantes que operan directamente en registros o memoria utilizando otros registros, memoria o constantes como el segundo agregado. No todas las combinaciones están disponibles (por ejemplo, agregar memoria a la memoria), pero hay suficientes para ser útiles. En cualquier caso, cualquier compilador con un optimizador decente sabrá generar el mismo código para elx = x + y
que lo haríax += y
.Es un buen modismo. Si es más rápido o no depende del idioma. En C, es más rápido porque se traduce en una instrucción para aumentar la variable en el lado derecho. Los lenguajes modernos, incluidos Python, Ruby, C, C ++ y Java, admiten la sintaxis op =. Es compacto y te acostumbras rápidamente. Como lo verá mucho en el código de otras personas (OPC), también puede acostumbrarse y usarlo. Esto es lo que sucede en otros idiomas.
En Python, escribir
x += 5
aún causa la creación del objeto entero 1 (aunque puede extraerse de un grupo) y la huérfana del objeto entero que contiene 5.En Java, hace que se produzca un lanzamiento tácito. Intenta escribir
fuente
x+=5
tiene tan poco significado comox=x+5
; Por ejemplo, en Haskell, este último no causax
que se incremente en 5, sino que incurre en un bucle de recursión infinito. ¿Quién querría una taquigrafía para eso?+=
no depende tanto del idioma como del procesador . Por ejemplo, X86 es una arquitectura de dos direcciones, solo es compatible de+=
forma nativa. Sea = b + c;
debe compilar una declaración comoa = b; a += c;
porque simplemente no hay instrucciones que puedan colocar el resultado de la adición en otro lugar que no sea el lugar de uno de los sumandos. La arquitectura Power, por el contrario, es una arquitectura de tres direcciones que no tiene comandos especiales para+=
. En esta arquitectura, las declaracionesa += b;
ya = a + b;
siempre se compilan con el mismo código.Los operadores como
+=
son muy útiles cuando está utilizando una variable como acumulador , es decir, un total acumulado :Es mucho más fácil de leer que:
En el primer caso, conceptualmente, estás modificando el valor en
x
. En el segundo caso, está calculando un nuevo valor y asignándolo ax
cada vez. Y aunque probablemente nunca escribiría código que sea tan simple, la idea sigue siendo la misma ... el foco está en lo que está haciendo a un valor existente en lugar de crear un valor nuevo.fuente
Considera esto
D0 realmente quieres escribir
fuente
Las otras respuestas se dirigen a los casos más comunes, pero hay otra razón: en algunos lenguajes de programación, se puede sobrecargar; por ejemplo, Scala .
Lección de Scala pequeña:
Si una clase solo define el
+
operador, dex+=y
hecho es un atajo dex=x+y
.Si una clase se sobrecarga
+=
, sin embargo, no son:Además, los operadores
+
y+=
son dos operadores separados (y no solo estos: + a, ++ a, a ++, a + ba + = b también son operadores diferentes); en idiomas donde la sobrecarga del operador está disponible, esto podría crear situaciones interesantes. Tal como se describió anteriormente: si sobrecarga al+
operador para realizar la adición, tenga en cuenta que+=
también tendrá que sobrecargarse.fuente
+=
y luego definira+b
como "hacer una copiaa
, ponerlab
usando+=
, devolver la copia dea
".Dilo una vez y solo una vez: en
x = x + 1
, digo 'x' dos veces.Pero nunca escriba 'a = b + = 1' o tendremos que matar 10 gatitos, 27 ratones, un perro y un hámster.
Nunca debe cambiar el valor de una variable, ya que facilita probar que el código es correcto; consulte la programación funcional. Sin embargo, si lo hace, entonces es mejor no decir las cosas solo una vez.
fuente