¿Por qué hay ninguna ++
y --
operadores en Python?
¿Por qué hay ninguna ++
y --
operadores en Python?
Respuestas:
No es porque no tiene sentido; tiene perfecto sentido definir "x ++" como "x + = 1, evaluando el enlace anterior de x".
Si desea conocer la razón original, tendrá que leer las viejas listas de correo de Python o preguntarle a alguien que estuvo allí (por ejemplo, Guido), pero es bastante fácil justificar el hecho:
Incremento y decremento simples no son necesarios tanto como en otros idiomas. No escribes cosas como for(int i = 0; i < 10; ++i)
en Python muy a menudo; en cambio haces cosas como for i in range(0, 10)
.
Como no se necesita con tanta frecuencia, hay muchas menos razones para darle su propia sintaxis especial; cuando necesita aumentar, +=
generalmente está bien.
No es una decisión si tiene sentido o si se puede hacer, lo hace y puede. Se trata de si vale la pena agregar el beneficio a la sintaxis central del lenguaje. Recuerde, se trata de cuatro operadores: postinc, postdec, preinc, predec, y cada uno de estos necesitaría tener sus propias sobrecargas de clase; todos necesitan ser especificados y probados; agregaría códigos de operación al lenguaje (lo que implica un motor VM más grande y, por lo tanto, más lento); cada clase que admita un incremento lógico necesitaría implementarlos (además de +=
y-=
).
Todo esto es redundante con +=
y -=
, por lo que se convertiría en una pérdida neta.
i
hacerlo directamente, si realmente lo necesita y no puede, por ejemplo, usarloarray.append()
i++
y ++i
...
++
y --
se utiliza de manera indefinida o no especificada. comportamiento. Permiten escribir código complicado, difícil de analizar correctamente.
Esta respuesta original que escribí es un mito del folklore de la informática. : Dennis Ritchie lo desacredita como "históricamente imposible" como se señala en las cartas a los editores de Comunicaciones de la ACM Julio 2012 doi: 10.1145 / 2209249.2209251
Los operadores de incremento / decremento de C se inventaron en un momento en que el compilador de C no era muy inteligente y los autores querían poder especificar la intención directa de utilizar un operador de lenguaje de máquina que ahorrara un puñado de ciclos para un compilador que podría hacer un
load memory
load 1
add
store memory
en vez de
inc memory
y el PDP-11 incluso admitía instrucciones de "autoincremento" y "autoincremento diferido" correspondientes a *++p
y*p++
, respectivamente. Vea la sección 5.3 del manual si es terriblemente curioso.
Como los compiladores son lo suficientemente inteligentes como para manejar los trucos de optimización de alto nivel integrados en la sintaxis de C, ahora son solo una conveniencia sintáctica.
Python no tiene trucos para transmitir intenciones al ensamblador porque no usa uno.
Siempre supuse que tenía que ver con esta línea del zen de Python:
Debe haber una, y preferiblemente solo una, forma obvia de hacerlo.
x ++ y x + = 1 hacen exactamente lo mismo, por lo que no hay razón para tener ambos.
one--
es cero?
one--
es uno en la oración, pero cero inmediatamente después. Entonces este 'koan' también sugiere que los operadores de incremento / decremento no son obvios.
Por supuesto, podríamos decir "Guido simplemente decidió de esa manera", pero creo que la pregunta es realmente sobre los motivos de esa decisión. Creo que hay varias razones:
Porque, en Python, los enteros son inmutables (int's + = en realidad devuelve un objeto diferente).
Además, con ++ / - debe preocuparse por el incremento / decremento previo versus posterior, y solo se necesita presionar una tecla más para escribir x+=1
. En otras palabras, evita la posible confusión a expensas de muy poca ganancia.
42++
... Algo como esto (modificar una constante literal) era realmente posible en algunos compiladores Fortran antiguos (o eso he leído): todos los usos futuros de ese literal en la ejecución de ese programa realmente tendría un valor diferente. ¡Feliz depuración!
int
en general sean inmutables. Una int
en C simplemente designa un lugar en la memoria. Y los bits en ese lugar son muy mutables. Puede, por ejemplo, crear una referencia de an int
y cambiar el referente de esa referencia. Este cambio es visible en todas las referencias (incluida la int
variable original ) a ese lugar. Lo mismo no es válido para un objeto entero Python.
Python tiene mucho que ver con la claridad y es probable que ningún programador adivine correctamente el significado a --a
menos que haya aprendido un lenguaje que tenga esa construcción.
Python también se trata de evitar construcciones que provocan errores y ++
se sabe que los operadores son fuentes ricas de defectos. Estas dos razones son suficientes para no tener esos operadores en Python.
La decisión de que Python use sangría para marcar bloques en lugar de medios sintácticos, como alguna forma de horquillado de inicio / fin o marcado final obligatorio, se basa principalmente en las mismas consideraciones.
Por ejemplo, eche un vistazo a la discusión sobre la introducción de un operador condicional (en C cond ? resultif : resultelse
:) en Python en 2005. Lea al menos el primer mensaje y el mensaje de decisión de esa discusión (que tenía varios precursores sobre el mismo tema anteriormente).
Curiosidades: La PEP mencionada con frecuencia en ella es la "Propuesta de extensión de Python" PEP 308 . LC significa comprensión de la lista , GE significa expresión del generador (y no te preocupes si eso te confunde, no son ninguno de los pocos puntos complicados de Python).
Mi comprensión de por qué Python no tiene ++
operador es la siguiente: cuando escriba esto en Python a=b=c=1
obtendrá tres variables (etiquetas) que apuntan al mismo objeto (cuyo valor es 1). Puede verificar esto utilizando la función id que devolverá una dirección de memoria de objeto:
In [19]: id(a)
Out[19]: 34019256
In [20]: id(b)
Out[20]: 34019256
In [21]: id(c)
Out[21]: 34019256
Las tres variables (etiquetas) apuntan al mismo objeto. Ahora incremente una de las variables y vea cómo afecta a las direcciones de memoria:
In [22] a = a + 1
In [23]: id(a)
Out[23]: 34019232
In [24]: id(b)
Out[24]: 34019256
In [25]: id(c)
Out[25]: 34019256
Puede ver que la variable a
ahora apunta a otro objeto como variables b
y c
. Porque lo has usado a = a + 1
está explícitamente claro. En otras palabras, asigna completamente otro objeto a la etiqueta a
. Imagine que puede escribir a++
, sugeriría que no asignó a la variablea
objeto nuevo sino que el ratter incrementa el antiguo. Todo esto es en mi humilde opinión para minimizar la confusión. Para una mejor comprensión, vea cómo funcionan las variables de Python:
¿Python es llamada por valor o llamada por referencia? Ninguno.
¿Python pasa por valor o por referencia?
¿Python pasa por referencia o pasa por valor?
Python: ¿Cómo paso una variable por referencia?
Comprender las variables de Python y la administración de memoria
Emulación del comportamiento de paso por valor en python
Fue diseñado de esa manera. Los operadores de incremento y decremento son solo atajos para x = x + 1
. Python generalmente ha adoptado una estrategia de diseño que reduce la cantidad de medios alternativos para realizar una operación. La asignación aumentada es lo más parecido a los operadores de incremento / decremento en Python, y ni siquiera se agregaron hasta Python 2.0.
return a[i++]
con return a[i=i+1]
.
Soy muy nuevo en Python, pero sospecho que la razón se debe al énfasis entre los objetos mutables e inmutables dentro del lenguaje. Ahora, sé que x ++ se puede interpretar fácilmente como x = x + 1, pero PARECE que estás incrementando en el lugar un objeto que podría ser inmutable.
Solo mi conjetura / sentimiento / corazonada.
x++
está más cerca de x += 1
que x = x + 1
, estos dos también hacen una diferencia en los objetos mutables.
Primero, Python solo está indirectamente influenciado por C; está fuertemente influenciado por ABC , que aparentemente no tiene estos operadores , por lo que tampoco debería ser una gran sorpresa no encontrarlos en Python.
En segundo lugar, como otros han dicho, el incremento y la disminución están respaldados por +=
y-=
ya.
Tercero, soporte completo para a ++
y--
conjunto operador generalmente incluye apoyar tanto el prefijo y versiones de sufijo de ellos. En C y C ++, esto puede conducir a todo tipo de construcciones "encantadoras" que me parecen (en mi opinión) contrarias al espíritu de simplicidad y sencillez que adopta Python.
Por ejemplo, si bien la declaración C while(*t++ = *s++);
puede parecer simple y elegante para un programador experimentado, para alguien que la está aprendiendo, es todo menos simple. Agregue una mezcla de incrementos y decrementos de prefijo y postfix, e incluso muchos profesionales tendrán que detenerse y pensar un poco.
Creo que se deriva del credo de Python que "explícito es mejor que implícito".
Esto puede deberse a que @GlennMaynard está analizando el asunto en comparación con otros lenguajes, pero en Python, usted hace las cosas al estilo python. No es una pregunta de "por qué". Está ahí y puedes hacer las cosas con el mismo efecto x+=
. En The Zen of Python , se da: "solo debería haber una forma de resolver un problema". Las opciones múltiples son excelentes en el arte (libertad de expresión) pero pésimas en ingeniería.
La ++
clase de operadores son expresiones con efectos secundarios. Esto es algo que generalmente no se encuentra en Python.
Por la misma razón, una asignación no es una expresión en Python, evitando así el if (a = f(...)) { /* using a here */ }
idioma común .
Por último, sospecho que el operador no es muy coherente con la semántica de referencia de Pythons. Recuerde, Python no tiene variables (o punteros) con la semántica conocida de C / C ++.
f(a)
donde a
hay una lista, algún objeto inmutable.
Quizás una mejor pregunta sería preguntar por qué estos operadores existen en C. K&R llama operadores de incremento y decremento 'inusuales' (Sección 2.8 página 46). La Introducción los llama "más concisos y, a menudo, más eficientes". Sospecho que el hecho de que estas operaciones siempre surgen en la manipulación del puntero también ha jugado un papel en su introducción. En Python, probablemente se decidió que no tenía sentido intentar optimizar los incrementos (de hecho, acabo de hacer una prueba en C, y parece que el ensamblado generado por gcc usa addl en lugar de incl en ambos casos) y no hay puntero aritmético; entonces habría sido solo una forma más de hacerlo y sabemos que Python detesta eso.
como lo entendí para que no pienses que el valor en la memoria ha cambiado. en c cuando haces x ++, el valor de x en la memoria cambia. pero en python todos los números son inmutables, por lo tanto, la dirección que x señaló como todavía tiene x no x + 1. cuando escribe x ++, pensaría que x cambiar lo que realmente sucede es que x referencia se cambia a una ubicación en la memoria donde se almacena x + 1 o recrear esta ubicación si no existe.
++
diferente += 1
?
Para completar respuestas ya buenas en esa página:
Supongamos que decidimos hacer esto, prefijo ( ++i
) que rompería los operadores unarios + y -.
Hoy, el prefijo por ++
o --
no hace nada, ya que habilita el operador unario más dos veces (no hace nada) o unario menos dos veces (dos veces: se cancela)
>>> i=12
>>> ++i
12
>>> --i
12
Entonces eso potencialmente rompería esa lógica.
Otras respuestas han descrito por qué no es necesario para los iteradores, pero a veces es útil cuando se asigna para aumentar una variable en línea, puede lograr el mismo efecto usando tuplas y asignaciones múltiples:
b = ++a
se convierte en:
a,b = (a+1,)*2
y se b = a++
convierte en:
a,b = a+1, a
Python 3.8 presenta el :=
operador de asignación , lo que nos permite lograr foo(++a)
con
foo(a:=a+1)
foo(a++)
Sin embargo, todavía es difícil de alcanzar.
Creo que esto se relaciona con los conceptos de mutabilidad e inmutabilidad de los objetos. 2,3,4,5 son inmutables en python. Consulte la imagen a continuación. 2 ha corregido la identificación hasta este proceso de Python.
x ++ esencialmente significaría un incremento en el lugar como C. En C, x ++ realiza incrementos en el lugar. Entonces, x = 3, y x ++ incrementaría 3 en la memoria a 4, a diferencia de python donde 3 todavía existirían en la memoria.
Por lo tanto, en Python, no necesita recrear un valor en la memoria. Esto puede conducir a optimizaciones de rendimiento.
Esta es una respuesta basada en la corazonada.
Sé que este es un hilo antiguo, pero el caso de uso más común para ++ i no está cubierto, que es la indexación manual de conjuntos cuando no hay índices proporcionados. Esta situación es la razón por la cual Python proporciona enumerate ()
Ejemplo: en cualquier idioma dado, cuando usa una construcción como foreach para iterar sobre un conjunto, por el bien del ejemplo, incluso diremos que es un conjunto desordenado y necesita un índice único para todo para distinguirlos, digamos
i = 0
stuff = {'a': 'b', 'c': 'd', 'e': 'f'}
uniquestuff = {}
for key, val in stuff.items() :
uniquestuff[key] = '{0}{1}'.format(val, i)
i += 1
En casos como este, python proporciona un método de enumeración, p. Ej.
for i, (key, val) in enumerate(stuff.items()) :