Pregunta Comportamiento de operadores de incremento y decremento en Python


Observé que se puede aplicar un operador de preincremento / decremento en una variable (como ++count) ¡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 visto en C / C ++?


593
2017-09-28 07:33


origen


Respuestas:


++ no es un operador. Son dos + operadores. los + operador es el identidad operador, que no hace nada. (Aclaración: el + y - los operadores unarios solo trabajan con números, pero supongo que no esperarías un hipotético ++ operador para trabajar en cadenas)

++count

Parses como

+(+count)

Lo que se traduce a

count

Tienes que usar el poco más largo += operador para hacer lo que quiere hacer:

count += 1

Sospecho que ++ y -- los operadores se quedaron fuera por coherencia y simplicidad. No sé el argumento exacto que dio Guido van Rossum para la decisión, pero puedo imaginar algunos argumentos:

  • Análisis más simple. Técnicamente, el análisis ++count es ambiguo, ya que podría ser +, +, count (dos únicos + operadores) tan fácilmente como podría ser ++, count (unario ++ operador). No es una ambigüedad sintáctica significativa, pero sí existe.
  • Lenguaje más simple. ++ no es más que un sinónimo de += 1. Era una abreviatura inventada porque los compiladores de C eran estúpidos y no sabían cómo optimizar a += 1 en el inc instrucción que tienen la mayoría de las computadoras. En este día de optimización de compiladores y códigos de bytes de lenguajes interpretados, agregar operadores a un lenguaje que permita a los programadores optimizar su código generalmente es desaprobado, especialmente en un lenguaje como Python que está diseñado para ser consistente y legible.
  • Confusos efectos secundarios. Un error común de principiante en idiomas con ++ los operadores están mezclando las diferencias (tanto en precedencia como en valor de retorno) entre los operadores pre y post incremento / decremento, y a Python le gusta eliminar los "gotcha" de lenguaje. los problemas de precedencia de pre / post-incremento en C son bastante peludas e increíblemente fáciles de estropear.

764
2017-09-28 07:39



Cuando desea incrementar o disminuir, normalmente desea hacer eso en un número entero. Al igual que:

b++

Pero en Python, los enteros son inmutable. Es decir que no puedes cambiarlos. Esto se debe a que los objetos enteros se pueden usar bajo varios nombres. Prueba esto:

>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True

ayb son en realidad el mismo objeto. Si incrementaste a, también incrementarías b. Eso no es lo que quieres. Entonces debes reasignar. Me gusta esto:

b = b + 1

O más simple:

b += 1

Que reasignará b a b+1. Eso no es un operador de incremento, porque no aumenta blo 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 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 usa cada vez que tiene un bucle, por ejemplo.


327
2017-09-28 09:05



Mientras que las otras respuestas son correctas en la medida en que muestran qué tan simple + usualmente lo hace (es decir, deja el número tal como está, si es uno), están incompletos en la medida en que no explican lo que sucede.

Para ser exacto, +x evalúa a x.__pos__() y ++x a x.__pos__().__pos__().

Podría imaginar una estructura de clase MUY rara (¡Niños, no hagas 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)

42
2018-06-26 14:59



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 a 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)

Disminuir operadores:

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

Usé estas funciones en mi módulo traduciendo javascript a python.


9
2017-10-05 15:37



En Python, una distinción entre expresiones y declaraciones es rígidamente   se aplica, a diferencia de idiomas como Common Lisp, Scheme o   Rubí.

Wikipedia

Entonces, al introducir dichos operadores, romperías la división expresión / declaración.

Por la misma razón no puedes escribir

if x = 0:
  y = 1

como puede en algunos otros idiomas donde tal distinción no se preserva.


7
2017-12-16 16:33



Sí, extrañé +++ y también la funcionalidad. Unos pocos millones de líneas de código c encerraron ese tipo de pensamiento en mi cabeza anterior, y en lugar de combatirlo ... Aquí hay una clase que improvisé que implementa:

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

Puede 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 un 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 comportamiento inesperado (y totalmente no deseado),

c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s

... porque dentro de esa tupla, obtiene el objeto() no es lo que se usa, en su lugar se pasa una referencia al objeto a la función de formateo. Suspiro. Asi que:

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s

... o, más ampliamente, y explícitamente lo que realmente queríamos que suceda, aunque está contraindicado en forma real por la verbosidad (uso c.v en lugar)...

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s

4
2018-05-19 20:40