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

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

Quel est le comportement des opérateurs pre-increment/decrement (++/--) en Python?

pourquoi Python dévie-t-il du comportement de ces opérateurs en C/C++?

612
demandé sur Alex.K. 2009-09-28 11:33:19

6 réponses

++ n'est pas un opérateur. Il s'agit de deux opérateurs + . L'opérateur + est l'opérateur identity , qui ne fait rien. (Clarification: les opérateurs unaires + et - ne fonctionnent que sur des nombres, mais je suppose que vous ne vous attendez pas à ce qu'un opérateur hypothétique ++ travaille sur des cordes.)

++count

Analyse

+(+count)

qui se traduit par

count

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

count += 1

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

  • simple parsing. Techniquement, le parsing ++count est ambigu, comme il pourrait être + , + , count (deux unaire + opérateurs) aussi facilement qu'elle pourrait être ++ , count (un unaire ++ de l'opérateur). Ce n'est pas une ambiguïté syntaxique significative, mais elle existe.
  • un langage plus Simple. ++ n'est rien de plus qu'un synonyme pour += 1 . C'était un raccourci inventé parce que C Compilateurs étaient stupides et ne savait pas comment optimiser a += 1 dans le inc l'instruction de la plupart des ordinateurs ont. En cette journée d'optimisation des compilateurs et des langages interprétés en bytecode, ajouter des 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.
  • effets secondaires confus. Une erreur commune de débutant dans les langues avec les opérateurs ++ est de mélanger les différences (à la fois dans la priorité et dans la valeur de retour) entre le pré - et post-increment/decrement operators, et Python aime éliminer le langage "gotcha" - s. Les précédents numéros de pré- / post-increment en C sont assez poilus, et incroyablement facile à gâcher.
794
répondu Chris Lutz 2018-05-24 15:00:26

quand vous voulez incrémenter ou décrémenter, vous voulez généralement le faire sur un entier. Comme ceci:

b++

mais en Python, les entiers sont immuable . C'est que vous ne pouvez pas les modifier. C'est parce que les objets entiers peuvent être utilisés sous plusieurs noms. Essayez ceci:

>>> 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 incrémentez a, vous incrémenterez aussi B. Ce n'est pas ce que tu veux. Si vous avoir à réaffecter. Comme ceci:

b = b + 1

ou plus simple:

b += 1

qui va réassigner b à b+1 . Ce n'est pas un opérateur d'accroissement, parce qu'il n'augmente pas b , il le réassigne.

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

338
répondu Lennart Regebro 2017-06-09 09:40:54

alors que les autres réponses sont correctes dans la mesure où elles montrent ce qu'un simple + fait habituellement (à savoir, laisser le nombre tel qu'il est, si c'est un), elles sont incomplètes dans la mesure où elles 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 faites pas ça à 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)
43
répondu glglgl 2012-06-26 14:59:33

Python n'a pas de ces opérateurs, mais si vous 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

Utilisation:

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 second argument si vous voulez changer la variable locale, sinon elle essaiera de changer 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()

aussi avec ces fonctions vous pouvez faire:

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

mais en mon avis est beaucoup plus claire:

x = 1
x+=1
print(x)

opérateurs de décrément:

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 traduisant javascript en python.

9
répondu Piotr Dabkowski 2014-10-05 15:37:15

en Python, une distinction entre expressions et déclarations est rigide en effet, contrairement aux langues telles que le Lisp commun, le Rubis.

Wikipedia

ainsi, en introduisant de tels opérateurs, vous briseriez la division expression/déclaration.

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

if x = 0:
  y = 1

comme vous pouvez dans certains autres les langues où une telle distinction n'est pas préservée.

7
répondu Vitalii Fedorenko 2012-01-25 13:47:23

Oui, j'ai manqué ++ et -- la fonctionnalité aussi. Quelques millions de lignes de code C ont gravé ce genre de pensée dans mon ancienne tête, et plutôt que de la combattre... Voici une classe que j'ai concocté qui met en œuvre:

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

Ici, c'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

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

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

....ou tout simplement...

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

...et pour (re-)en entier...

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

...bien que cela maintienne le compteur de type C:

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

EDIT:

et puis il y a ce peu de comportement inattendu (et tout à fait 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, getitem () n'est pas ce qui est utilisé, à la place d'une référence à l'objet est passé à la fonction de formatage. Soupir. So:

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

...ou, plus verbeusement, et explicitement ce que nous voulions réellement arriver, bien que contre-indiqué dans la forme réelle par la verbosité (utilisez c.v à la place)...

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s
4
répondu fyngyrz 2015-05-19 21:33:40