Je remarque qu'un opérateur de pré-incrémentation/décrémentation peut être appliqué à une variable (comme ++count
). Cela compile, mais cela ne change pas réellement la valeur de la variable!
Quel est le comportement des opérateurs de pré-incrémentation/décrémentation (++/-) en Python?
Pourquoi Python dévie-t-il du comportement de ces opérateurs vu en C/C++?
++
n'est pas un opérateur. Ce sont deux opérateurs +
. L'opérateur +
est l'opérateur identity, qui ne fait rien. (Précision: les opérateurs unaires +
et -
ne fonctionnent que sur des nombres, mais je suppose que vous ne vous attendriez pas à ce qu'un opérateur ++
hypothétique travaille sur des chaînes.)
++count
Analyses comme
+(+count)
Ce 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é laissés de côté pour des raisons 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:
++count
est ambiguë, dans la mesure où il pourrait s'agir de +
, +
, count
(deux opérateurs unaires +
) aussi facilement qu'il pourrait l'être ++
, count
(un opérateur unaire ++
). Ce n'est pas une ambiguïté syntaxique significative, mais cela existe.++
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 les instructions inc
de la plupart des ordinateurs. En cette journée d'optimisation des compilateurs et des langages interprétés en bytecode, l'ajout d'opérateurs à un langage permettant aux programmeurs d'optimiser leur code est généralement mal vu, en particulier dans un langage tel que Python, conçu pour être cohérent et lisible.++
est de mélanger les différences (de priorité et de valeur de retour) entre les opérateurs avant et après l'incrémentation/décrémentation, et Python aime éliminer les "gotcha" de langues. Les problèmes de précédence de pré/post-incrémentation en C _ sont plutôt poilus et incroyablement faciles à gâcher.Lorsque vous souhaitez incrémenter ou décrémenter, vous souhaitez généralement le faire sur un entier. Ainsi:
b++
Mais en Python, les entiers sont immuable . C'est que vous ne pouvez pas les changer. En effet, 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 augmentez a, vous augmenterez également b. Ce n'est pas ce que tu veux. Donc, vous devez réaffecter. Comme ça:
b = b + 1
Ou plus simple:
b += 1
Ce qui réaffectera b
à b+1
. Ce n'est pas un opérateur d'incrémentation, car il n'incrémente pas b
, il le réaffecte.
En bref: Python se comporte différemment ici, car ce n'est pas du C, ce 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.
Alors que les autres réponses sont correctes dans la mesure où elles montrent ce que fait habituellement un simple +
(à savoir, laissez le numéro tel quel, s'il en est un), elles sont incomplètes dans la mesure où elles n'expliquent pas ce qui se passe.
Pour être exact, +x
est évalué à x.__pos__()
et ++x
à x.__pos__().__pos__()
.
Je pourrais imaginer une structure de classe TRÈS étrange (Enfants, ne fais 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)
Python ne possède pas ces opérateurs, mais si vous en avez vraiment besoin, vous pouvez écrire une fonction ayant les mêmes fonctionnalités.
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 () en tant que second argument si vous souhaitez modifier la variable locale, sinon le système tentera 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)
Opérateurs de décrémentation:
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 pour traduire javascript en python.
En Python, une distinction entre les expressions et les déclarations est rigide forcé, contrairement aux langages tels que Common LISP, Scheme ou Rubis.
Donc, en introduisant de tels opérateurs, vous diviseriez la division expression/déclaration.
Pour la même raison, vous ne pouvez pas écrire
if x = 0:
y = 1
comme vous pouvez le faire dans d'autres langues où une telle distinction n'est pas préservée.
Oui, j'ai raté ++ et - la fonctionnalité aussi. Quelques millions de lignes de code c ont ancré ce type de pensée dans mon ancienne tête, et plutôt que de le combattre ... Voici une classe que j'ai bricolée et qui implémente:
pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.
Voici le:
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 pouvez 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é) affectation 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
... 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 peu de comportement inattendu (et totalement 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é, une référence à l'objet est transmise à la fonction de formatage. Soupir. Alors:
c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s
... ou, plus verbalement et explicitement, ce que nous voulions réellement arriver, bien que contre-indiqué dans sa forme actuelle par la verbosité (utilisez plutôt c.v
) ...
c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s
Python n'a pas d'opérateur unaire d'incrémentation/décrémentation (--
++
). Au lieu de cela, pour incrémenter une valeur, utilisez
a += 1
Mais soyez prudent ici. Si vous venez de C, même cela est différent en python. Python n'a pas de "variables" au sens de C, python utilise _/names et objects, et python int
s est immuable.
alors disons que vous faites
a = 1
Cela signifie en python: créez un objet de type int
ayant la valeur 1
et associez-lui le nom a
. Object est une instance de int
ayant la valeur 1
, et namea
y fait référence. Le nom a
et l'objet auquel il fait référence sont distincts.
Maintenant, disons que vous faites
a += 1
Puisque int
s est immuable, voici ce qui se passe:
a
fait référence (il s'agit d'une int
avec id 0x559239eeb380
)0x559239eeb380
(c'est 1
)int
avec la valeur 2
(il a l'identifiant d'objet 0x559239eeb3a0
)a
à ce nouvel objeta
fait référence à l'objet 0x559239eeb3a0
et l'objet d'origine (0x559239eeb380
) n'est plus désigné par le nom a
. S'il n'y a pas d'autres noms faisant référence à l'objet d'origine, les ordures seront récupérées ultérieurement.Essayez vous-même:
a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))