wake-up-neo.net

Verhalten von Inkrement- und Dekrementoperatoren in Python

Ich stelle fest, dass ein Pre-Increment/Decrement-Operator auf eine Variable angewendet werden kann (wie ++count). Es wird kompiliert, ändert jedoch nicht wirklich den Wert der Variablen!

Wie verhalten sich die Pre-Increment/Decrement-Operatoren (++/-) in Python? 

Warum weicht Python von dem Verhalten dieser Operatoren in C/C++ ab?

667
Ashwin Nanjappa

++ ist kein Operator. Es sind zwei +-Operatoren. Der Operator + ist der Operator identity, der nichts tut. (Erläuterung: Die unären Operatoren + und - bearbeiten nur Zahlen, aber ich gehe davon aus, dass Sie nicht erwarten würden, dass ein hypothetischer ++-Operator an Zeichenfolgen arbeitet.)

++count

Parses als

+(+count)

Was übersetzt in

count

Sie müssen den etwas längeren +=-Operator verwenden, um das zu tun, was Sie möchten:

count += 1

Ich vermute, die Operatoren ++ und -- wurden aus Gründen der Konsistenz und Einfachheit ausgelassen. Ich kenne das genaue Argument, das Guido van Rossum für die Entscheidung gegeben hat, nicht, aber ich kann mir ein paar Argumente vorstellen:

  • Einfacheres Parsen Technisch gesehen ist das Analysieren von ++count mehrdeutig, da es +, +, count (zwei unäre +-Operatoren) genauso einfach sein könnte wie ++, count (ein unärer ++-Operator). Es ist keine signifikante syntaktische Mehrdeutigkeit, aber sie existiert.
  • Einfachere Sprache. ++ ist nichts anderes als ein Synonym für += 1. Diese Abkürzung wurde erfunden, weil C-Compiler dumm waren und nicht wussten, wie sie a += 1 für die inc-Anweisung der meisten Computer optimieren. An diesem Tag der Optimierung von Compilern und durch Bytecode interpretierten Sprachen ist das Hinzufügen von Operatoren zu einer Sprache, mit denen Programmierer ihren Code optimieren können, in der Regel verpönt, insbesondere in einer Sprache wie Python, die konsistent und lesbar ist.
  • Verwirrende Nebenwirkungen. Ein häufiger Neuling-Fehler in Sprachen mit ++-Operatoren besteht darin, die Unterschiede (sowohl in der Rangfolge als auch im Rückgabewert) zwischen den Operatoren vor und nach dem Inkrementieren/Dekrementieren zu vertauschen. Die Prioritätsprobleme von Pre-/Post-Inkrement in C sind ziemlich haarig und unglaublich einfach zu verwirren.
875
Chris Lutz

Wenn Sie inkrementieren oder dekrementieren möchten, möchten Sie dies normalerweise für eine ganze Zahl. So wie:

b++

In Python sind ganze Zahlen unveränderlich . Das heißt, du kannst sie nicht ändern. Dies liegt daran, dass die Integer-Objekte unter mehreren Namen verwendet werden können. Versuche dies:

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

a und b oben sind eigentlich das gleiche Objekt. Wenn Sie a inkrementieren würden, würden Sie auch b erhöhen. Das willst du nicht. Sie müssen also neu zuweisen. So was:

b = b + 1

Oder einfacher:

b += 1

Dadurch wird b dem b+1 zugewiesen. Dies ist kein Inkrementierungsoperator, da b nicht inkrementiert wird, sondern es neu zugewiesen wird.

Kurz gesagt: Python verhält sich hier anders, weil es kein C ist und kein Wrapper für den Maschinencode ist, sondern eine dynamische Sprache, in der Inkremente keinen Sinn ergeben und auch nicht so notwendig sind wie in C , wo Sie sie beispielsweise jedes Mal verwenden, wenn Sie eine Schleife haben.

351
Lennart Regebro

Während die Antworten der anderen insofern richtig sind, als sie zeigen, was ein bloßer + normalerweise tut (dh, die Nummer so belassen, wie sie ist, wenn es eine ist), sind sie unvollständig, da sie nicht erklären, was passiert.

Um genau zu sein, wertet +x zu x.__pos__() und ++x zu x.__pos__().__pos__() aus.

Ich könnte mir eine sehr seltsame Klassenstruktur vorstellen (Kinder, mach das nicht zu Hause!):

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)
47
glglgl

Python hat diese Operatoren nicht, aber wenn Sie sie wirklich brauchen, können Sie eine Funktion schreiben, die die gleiche Funktionalität hat.

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

Verwendungszweck:

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

In einer Funktion müssen Sie locals () als zweites Argument hinzufügen, wenn Sie die lokale Variable ändern möchten. Andernfalls wird versucht, global zu ändern.

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

Auch mit diesen Funktionen können Sie:

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

Meiner Meinung nach ist der folgende Ansatz jedoch viel klarer:

x = 1
x+=1
print(x)

Dekrementoperatoren:

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

Ich habe diese Funktionen in meinem Modul verwendet, um Javascript in Python zu übersetzen.

9
Piotr Dabkowski

In Python ist der Unterschied zwischen Ausdrücken und Anweisungen streng erzwungen, im Gegensatz zu Sprachen wie Common LISP, Scheme oder Rubin.

Wikipedia

Indem Sie solche Operatoren einführen, würden Sie also die Ausdrucks-/Anweisungsaufteilung durchbrechen. 

Aus dem gleichen Grund können Sie nicht schreiben

if x = 0:
  y = 1

wie in anderen Sprachen, in denen diese Unterscheidung nicht erhalten bleibt.

8

Ja, ich habe auch ++ und - Funktionalität vermisst. Ein paar Millionen Zeilen c-Code enthielten diese Art von Denken in meinem alten Kopf, und anstatt dagegen zu kämpfen ... Hier ist eine Klasse, die ich zusammengeschnappt habe:

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

Hier ist es:

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

Sie könnten es so verwenden:

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

... schon mit c könnte man das machen ...

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

....oder nur...

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

... und zur (Neu-) Zuweisung in Ganzzahl ...

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

... während dies c als Typzähler beibehält:

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

BEARBEITEN:

Und dann gibt es dieses unerwartete (und durchaus unerwünschte) Verhalten ,

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

... weil in diesem Tuple getitem () nicht verwendet wird, sondern ein Verweis auf das Objekt an die Formatierungsfunktion übergeben wird. Seufzer. So:

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

... oder ausführlicher und explizit, was wir eigentlich erreichen wollten, obwohl dies in tatsächlicher Form durch die Ausführlichkeit (durch Verwendung von c.v) angezeigt wird ...

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

TL; DR

Python hat keine unären Inkrementierungs-/Dekrementierungsoperatoren (--++). Verwenden Sie stattdessen einen Wert, um einen Wert zu erhöhen 

a += 1

Weitere Details und Faden

Aber sei hier vorsichtig. Wenn Sie von C kommen, ist dies auch in Python anders. Python hat keine "Variablen" im Sinne von C, stattdessen verwendet Python _/names und objects und in Python sind ints unveränderlich.

also lassen Sie uns sagen, dass Sie tun

a = 1

Das bedeutet in Python: Erstellen Sie ein Objekt vom Typ int mit dem Wert 1 und binden Sie den Namen a daran. Das object ist eine Instanz von int mit dem Wert 1, und der namea verweist darauf. Der Name a und das Objekt, auf das er verweist, sind unterschiedlich.

Lassen Sie uns jetzt sagen, Sie tun 

a += 1

Da ints unveränderlich sind, geschieht Folgendes:

  1. nach dem Objekt suchen, auf das sich a bezieht (es ist eine int mit der ID 0x559239eeb380)
  2. den Wert des Objekts 0x559239eeb380 nachschlagen (es ist 1)
  3. addiere 1 zu diesem Wert (1 + 1 = 2)
  4. erstellen Sie ein neues int-Objekt mit dem Wert 2 (es hat die Objekt-ID 0x559239eeb3a0).
  5. binden Sie den Namen a an dieses neue Objekt
  6. Nun bezieht sich a auf das Objekt 0x559239eeb3a0 und das ursprüngliche Objekt (0x559239eeb380) wird nicht mehr mit dem Namen a referenziert. Wenn sich keine anderen Namen auf das ursprüngliche Objekt beziehen, wird der Müll später gesammelt.

Probieren Sie es selbst aus:

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
0
RBF06