Question Comportement des opérateurs d'incrémentation et de décrémentation en Python


Je remarque qu'un opérateur de pré-incrémentation / décrémentation peut être appliqué sur une variable (comme ++count). Il compile, mais cela ne change pas la valeur de la variable!

Quel est le comportement des opérateurs de pré-incrémentation / décrémentation (++ / -) en Python?

Pourquoi Python dévier du comportement de ces opérateurs vu en C / C ++?


593
2017-09-28 07:33


origine


Réponses:


++ n'est pas un opérateur. C'est deux + les opérateurs. le + l'opérateur est le identité opérateur, qui ne fait rien. (Clarification: le + et - opérateurs unaires ne travaillent que sur les chiffres, mais je suppose que vous ne vous attendez pas à un hypothétique ++ opérateur pour travailler sur les chaînes.)

++count

Analyse comme

+(+count)

Qui se traduit par

count

Vous devez utiliser le légèrement plus long += opérateur pour faire ce que vous voulez faire:

count += 1

Je soupçonne la ++ et -- les opérateurs ont été exclus par souci de cohérence et de simplicité. Je ne connais pas l'argument exact que Guido van Rossum a donné pour la décision, mais je peux imaginer quelques arguments:

  • Analyse plus simple. Techniquement, l'analyse ++count est ambigu, comme il pourrait être +, +, count (deux unaires + opérateurs) aussi facilement que cela pourrait être ++, count (un unaire ++ opérateur). Ce n'est pas une ambiguïté syntaxique significative, mais ça existe.
  • Langage plus simple. ++ n'est rien de plus qu'un synonyme de += 1. C'était un raccourci inventé parce que les compilateurs C étaient stupides et ne savaient pas comment optimiser a += 1 dans le inc instruction la plupart des ordinateurs ont. En ce jour d'optimisation des compilateurs et des langages interprétés bytecode, l'ajout d'opérateurs à un langage pour permettre aux programmeurs d'optimiser leur code est généralement mal vu, surtout dans un langage comme Python qui est conçu pour être cohérent et lisible.
  • Des effets secondaires déroutants. Une erreur de débutant commune dans les langues avec ++ Les opérateurs mélangent les différences (à la fois en préséance et en valeur de retour) entre les opérateurs pré- et post-incrément / décrément, et Python aime éliminer le langage "gotcha" -s. le problèmes de priorité de pré / post-incrément en C sont assez poilus, et incroyablement facile à gâcher.

764
2017-09-28 07:39



Lorsque vous voulez incrémenter ou décrémenter, vous voulez généralement faire cela sur un entier. Ainsi:

b++

Mais en Python, les entiers sont immuable. C'est que vous ne pouvez pas les changer. C'est parce que les objets entiers peuvent être utilisés sous plusieurs noms. Essaye ça:

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

a et b ci-dessus sont en fait le même objet. Si vous avez incrémenté a, vous incrémenteriez aussi b. Ce n'est pas ce que tu veux. Donc, vous devez réaffecter. Comme ça:

b = b + 1

Ou plus simple:

b += 1

Qui va réaffecter b à b+1. Ce n'est pas un opérateur d'incrément, car il n'incrémente pas b, il le réaffecte.

En bref: Python se comporte différemment ici, car ce n'est pas un C, et n'est pas un wrapper de bas niveau autour du code machine, mais un langage dynamique de haut niveau, où les incréments n'ont pas de sens, et ne sont pas aussi nécessaires qu'en C , où vous les utilisez chaque fois que vous avez une boucle, par exemple.


327
2017-09-28 09:05



Alors que les autres réponses sont correctes dans la mesure où ils montrent ce que + fait habituellement (à savoir, laisser le numéro tel quel, s'il est un), ils sont incomplets dans la mesure où ils n'expliquent pas ce qui se passe.

Pour être exact, +x évalue à x.__pos__() et ++x à x.__pos__().__pos__().

Je pourrais imaginer une structure de classe très étrange (les enfants, ne le faites pas à la maison!) Comme ceci:

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 n'a pas ces opérateurs, mais si vous en avez vraiment besoin, vous pouvez écrire une fonction ayant la même fonctionnalité.

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

Usage:

x = 1
y = PreIncrement('x') #y and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2

Dans une fonction, vous devez ajouter locals () comme deuxième argument si vous voulez changer la variable locale, sinon elle va essayer de changer globalement.

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

Aussi avec ces fonctions, vous pouvez faire:

x = 1
print(PreIncrement('x'))   #print(x+=1) is illegal!

Mais à mon avis, l'approche suivante est beaucoup plus claire:

x = 1
x+=1
print(x)

Décrémenter les opérateurs:

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

J'ai utilisé ces fonctions dans mon module en traduisant javascript en python.


9
2017-10-05 15:37



En Python, une distinction entre les expressions et les déclarations est rigidement   imposé, contrairement aux langages tels que Common Lisp, Scheme ou   Rubis.

Wikipédia

Donc en introduisant de tels opérateurs, vous casseriez le partage expression / instruction.

Pour la même raison que vous ne pouvez pas écrire

if x = 0:
  y = 1

comme vous pouvez dans d'autres langues où cette distinction n'est pas conservée.


7
2017-12-16 16:33



Oui, j'ai raté ++ et - fonctionnalité aussi. Quelques millions de lignes de code ont ancré ce genre de pensée dans ma vieille tête, et plutôt que de la combattre ... Voici une classe que j'ai mise en place:

pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.

Ici 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

Vous pourriez l'utiliser comme ceci:

c = counter()                          # defaults to zero
for listItem in myList:                # imaginary task
     doSomething(c.postinc(),listItem) # passes c, but becomes c+1

... ayant déjà c, vous pourriez le faire ...

c.set(11)
while c.predec() > 0:
    print c

....ou juste...

d = counter(11)
while d.predec() > 0:
    print d

... et pour (ré) assignation en entiers ...

c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323

... alors que cela maintiendra c comme compteur de type:

c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323

MODIFIER:

Et puis il y a ce comportement inattendu (et complètement indésirable),

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

... parce qu'à l'intérieur de ce tuple, obtenir l'article() n'est pas ce qui est utilisé, à la place une référence à l'objet est passée à la fonction de formatage. Soupir. Alors:

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

... ou, plus explicitement, et explicitement ce que nous voulions réellement arriver, bien que contre-indiqué dans sa forme actuelle par la verbosité (utiliser c.v au lieu)...

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

4
2018-05-19 20:40