sentencias - Comportamiento de los operadores de incremento y decremento en Python
sentencias en python (6)
En Python, una distinción entre expresiones y declaraciones se aplica rígidamente, en contraste con lenguajes como Common Lisp, Scheme o Ruby.
Por lo tanto, al introducir dichos operadores, se rompería la expresión / división dividida.
Por la misma razón no puedes escribir
if x = 0:
y = 1
como se puede en otros idiomas donde no se conserva tal distinción.
Observo que un operador de preincremento / decremento puede aplicarse a una variable (como ++count
). ¡Se compila, pero en realidad no cambia el valor de la variable!
¿Cuál es el comportamiento de los operadores de preincremento / decremento (++ / -) en Python?
¿Por qué Python se desvía del comportamiento de estos operadores vistos en C / C ++?
Cuando desea aumentar o disminuir, normalmente desea hacer eso en un entero. Al igual que:
b++
Pero en Python, los enteros son inmutables . Eso es que no puedes cambiarlos. Esto se debe a que los objetos enteros se pueden usar con varios nombres. Prueba esto:
>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True
A y B de arriba son en realidad el mismo objeto. Si incrementara a, también incrementaría b. Eso no es lo que quieres. Así que tienes que reasignar. Me gusta esto:
b = b + 1
O más simple:
b += 1
Lo cual reasignará b
a b+1
. Eso no es un operador de incremento, porque no incrementa b
, lo reasigna.
En resumen: Python se comporta de manera diferente aquí, porque no es C, y no es un envoltorio de bajo nivel alrededor del código de máquina, sino un lenguaje dinámico de alto nivel, donde los incrementos no tienen sentido, y tampoco son tan necesarios como en C , donde los usas cada vez que tienes un bucle, por ejemplo.
Mientras que las otras respuestas son correctas en la medida en que muestran lo que normalmente hace un mero +
(es decir, dejar el número como está, si es uno), están incompletas en la medida en que no explican lo que sucede.
Para ser exactos, +x
evalúa a x.__pos__()
y ++x
a x.__pos__().__pos__()
.
Podría imaginar una estructura de clase MUY extraña (niños, ¡no hagan esto en casa!) Así:
class ValueKeeper(object):
def __init__(self, value): self.value = value
def __str__(self): return str(self.value)
class A(ValueKeeper):
def __pos__(self):
print ''called A.__pos__''
return B(self.value - 3)
class B(ValueKeeper):
def __pos__(self):
print ''called B.__pos__''
return A(self.value + 19)
x = A(430)
print x, type(x)
print +x, type(+x)
print ++x, type(++x)
print +++x, type(+++x)
Python no tiene estos operadores, pero si realmente los necesita, puede escribir una función que tenga la misma funcionalidad.
def PreIncrement(name, local={}):
#Equivalent to ++name
if name in local:
local[name]+=1
return local[name]
globals()[name]+=1
return globals()[name]
def PostIncrement(name, local={}):
#Equivalent to name++
if name in local:
local[name]+=1
return local[name]-1
globals()[name]+=1
return globals()[name]-1
Uso:
x = 1
y = PreIncrement(''x'') #y and x are both 2
a = 1
b = PostIncrement(''a'') #b is 1 and a is 2
Dentro de una función, debe agregar locals () como segundo argumento si desea cambiar la variable local, de lo contrario intentará cambiar global.
x = 1
def test():
x = 10
y = PreIncrement(''x'') #y will be 2, local x will be still 10 and global x will be changed to 2
z = PreIncrement(''x'', locals()) #z will be 11, local x will be 11 and global x will be unaltered
test()
También con estas funciones puedes hacer:
x = 1
print(PreIncrement(''x'')) #print(x+=1) is illegal!
Pero en mi opinión, el siguiente enfoque es mucho más claro:
x = 1
x+=1
print(x)
Operadores de decremento:
def PreDecrement(name, local={}):
#Equivalent to --name
if name in local:
local[name]-=1
return local[name]
globals()[name]-=1
return globals()[name]
def PostDecrement(name, local={}):
#Equivalent to name--
if name in local:
local[name]-=1
return local[name]+1
globals()[name]-=1
return globals()[name]+1
Utilicé estas funciones en mi módulo traduciendo javascript a python.
Sí, también me perdí la funcionalidad ++ y -. Unos pocos millones de líneas de código C grabaron ese tipo de pensamiento en mi cabeza anterior, y en lugar de combatirlo ... Aquí hay una clase que construí:
pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.
Aquí está:
class counter(object):
def __init__(self,v=0):
self.set(v)
def preinc(self):
self.v += 1
return self.v
def predec(self):
self.v -= 1
return self.v
def postinc(self):
self.v += 1
return self.v - 1
def postdec(self):
self.v -= 1
return self.v + 1
def __add__(self,addend):
return self.v + addend
def __sub__(self,subtrahend):
return self.v - subtrahend
def __mul__(self,multiplier):
return self.v * multiplier
def __div__(self,divisor):
return self.v / divisor
def __getitem__(self):
return self.v
def __str__(self):
return str(self.v)
def set(self,v):
if type(v) != int:
v = 0
self.v = v
Podrías usarlo así:
c = counter() # defaults to zero
for listItem in myList: # imaginary task
doSomething(c.postinc(),listItem) # passes c, but becomes c+1
... ya teniendo c, podrías hacer esto ...
c.set(11)
while c.predec() > 0:
print c
....o solo...
d = counter(11)
while d.predec() > 0:
print d
... y para (re) asignación en entero ...
c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type ''int''> 323
... mientras esto mantendrá c como contador de tipo:
c = counter(100)
c.set(c + 223)
print type(c),c # <class ''__main__.counter''> 323
EDITAR:
Y luego está este poco de comportamiento inesperado (y completamente no deseado) ,
c = counter(42)
s = ''%s: %d'' % (''Expecting 42'',c) # but getting non-numeric exception
print s
... porque dentro de esa tupla, getitem () no es lo que se usa, en cambio, se pasa una referencia al objeto a la función de formato. Suspiro. Asi que:
c = counter(42)
s = ''%s: %d'' % (''Expecting 42'',c.v) # and getting 42.
print s
... o, más verbalmente, y explícitamente lo que realmente queríamos que sucediera, aunque está en contraposición en forma real por la verbosidad (use cv
lugar) ...
c = counter(42)
s = ''%s: %d'' % (''Expecting 42'',c.__getitem__()) # and getting 42.
print s
++
no es un operador. Se trata de dos operadores +
El operador +
es el operador de identidad , que no hace nada. (Aclaración: los operadores +
y -
unarios solo funcionan con números, pero supongo que no esperaría que un operador hipotético ++
funcione con cadenas).
++count
Analiza como
+(+count)
Que se traduce en
count
Tienes que usar el operador ligeramente más largo +=
para hacer lo que quieres hacer:
count += 1
Sospecho que los operadores ++
y --
quedaron fuera por coherencia y simplicidad. No sé el argumento exacto que Guido van Rossum dio para la decisión, pero puedo imaginar algunos argumentos:
- Análisis más sencillo. Técnicamente, analizar el
++count
es ambiguo, ya que podría ser+
,+
,count
(dos operadores unarios+
) con la misma facilidad que podría ser++
,count
(un operador unario++
). No es una ambigüedad sintáctica significativa, pero existe. - Lenguaje más sencillo.
++
no es más que un sinónimo para+= 1
. Fue una taquigrafía inventada porque los compiladores de C eran estúpidos y no sabían cómo optimizara += 1
en la instruccióninc
tienen la mayoría de las computadoras. En este día de optimización de los compiladores y los lenguajes interpretados por el código de bytes, agregar operadores a un lenguaje para permitir que los programadores optimicen su código suele ser mal visto, especialmente en un lenguaje como Python que está diseñado para ser coherente y legible. - Efectos secundarios confusos. Un error de principiante común en lenguajes con operadores
++
es mezclar las diferencias (tanto en precedencia como en valor de retorno) entre los operadores pre y post incremento / decremento, y a Python le gusta eliminar el idioma "gotcha" -s. Los problemas de precedencia de pre- / post-incremento en C son bastante vellosos, y son increíblemente fáciles de desordenar.