Verhalten von Inkrement- und Dekrementoperatoren in Python

Lesezeit: 11 Minuten

Benutzer-Avatar
Ashwin Nanjappa

Wie verwende ich Prä-Inkrement/Dekrement-Operatoren (++, --), genau wie in C++?

Warum tut ++count ausführen, aber den Wert der Variablen nicht ändern?

  • Chris: Du hast meine Frage beantwortet (das was). Außerdem würde ich gerne wissen, warum sich Python in diesem Verhalten von C/C++ unterscheidet.

    – Ashwin Nanjappa

    28. September 2009 um 8:04 Uhr

  • Python ist nicht C oder C++. Verschiedene Designentscheidungen gingen in die Erstellung der Sprache ein. Insbesondere definiert Python bewusst keine Zuweisungsoperatoren, die in einem beliebigen Ausdruck verwendet werden können; vielmehr gibt es Zuweisungsanweisungen und erweiterte Zuweisungsanweisungen. Siehe Referenz unten.

    – Ned Deily

    28. September 2009 um 8:22 Uhr

  • Kaizer: Von C/C++ kommend, schreibe ich ++count und es wird in Python kompiliert. Also dachte ich, die Sprache hat die Operatoren.

    – Ashwin Nanjappa

    28. September 2009 um 9:47 Uhr

  • Angesichts der Tatsache, dass die meisten modernen OOP-Sprachen diese Symbole hatten, als GVR Python entwickelte, wäre es nicht sinnvoll, eine Syntaxwarnung für dieses Konstrukt einzuschließen?

    – Fuchs

    3. Mai 2014 um 6:44 Uhr

  • @mehaase ++ und — existieren in c nicht “als syntaktischer Zucker für die Zeigerarithmetik”, sie existieren, weil viele Prozessoren automatische Inkrement- und Dekrementspeicherzugriffsmechanismen (im Allgemeinen Zeigerindizierung, Stapelindizierung) als Teil ihrer nativen Anweisung haben einstellen. Zum Beispiel in 6809 Assembler: sta x++ …die daraus resultierende atomare Anweisung speichert die a Akku wo x zeigt, dann inkrementiert x durch die Größe des Akkumulators. Dies geschieht, weil es schneller als die Zeigerarithmetik ist, weil es sehr verbreitet ist und weil es leicht zu verstehen ist. Sowohl Pre- als auch Post.

    – fyngyrz

    28. Oktober 2016 um 13:16 Uhr

Benutzer-Avatar
Chris Lutz

++ ist kein Betreiber. Es ist zwei + Betreiber. Das + Betreiber ist der Identität Betreiber, der nichts tut. (Klarstellung: die + und - Unäre Operatoren arbeiten nur mit Zahlen, aber ich nehme an, Sie würden keine Hypothese erwarten ++ Operator zum Bearbeiten von Strings.)

++count

Analysiert als

+(+count)

Was übersetzt bedeutet

count

Sie müssen die etwas länger verwenden += Betreiber zu tun, was Sie tun möchten:

count += 1

Ich vermute die ++ und -- Operatoren wurden aus Gründen der Konsistenz und Einfachheit weggelassen. Ich kenne die genauen Argumente, die Guido van Rossum für die Entscheidung vorgebracht hat, nicht, aber ich kann mir einige Argumente vorstellen:

  • Einfachere Analyse. Technisch gesehen parsen ++count ist mehrdeutig, wie es sein könnte +, +, count (zwei unäre + Operatoren) so einfach wie möglich ++, count (eine unäre ++ Operator). Es ist keine signifikante syntaktische Mehrdeutigkeit, aber es existiert.
  • Einfachere Sprache. ++ ist nichts weiter als ein Synonym für += 1. Es war eine Abkürzung, die erfunden wurde, weil C-Compiler dumm waren und nicht wussten, wie man optimiert a += 1 in die inc Anleitung haben die meisten Computer. In der heutigen Zeit der Optimierung von Compilern und Bytecode-interpretierten Sprachen ist das Hinzufügen von Operatoren zu einer Sprache, mit denen Programmierer ihren Code optimieren können, normalerweise verpönt, insbesondere in einer Sprache wie Python, die darauf ausgelegt ist, konsistent und lesbar zu sein.
  • Verwirrende Nebenwirkungen. Ein häufiger Anfängerfehler in Sprachen mit ++ Operatoren verwechselt die Unterschiede (sowohl in der Priorität als auch im Rückgabewert) zwischen den Prä- und Post-Inkrement/Dekrement-Operatoren, und Python eliminiert gerne sprachliche “Gotcha”-s. Das Vorrang Probleme von Pre-/Post-Increment in C sind ziemlich haarig und unglaublich leicht zu vermasseln.

  • “Der +-Operator ist der “Identitäts”-Operator, der nichts tut.” Nur für numerische Typen; für andere Typen ist es standardmäßig ein Fehler.

    – neues Konto

    28. September 2009 um 7:47 Uhr

  • Beachten Sie auch, dass += und Freunde in Python keine Operatoren sind, die in Ausdrücken verwendet werden können. Vielmehr werden sie in Python als Teil einer “erweiterten Zuweisungsanweisung” definiert. Dies steht im Einklang mit der Sprachdesign-Entscheidung in Python, keine Zuweisung (“=”) als Operator innerhalb beliebiger Ausdrücke zuzulassen, im Gegensatz zu dem, was man in C tun kann. Siehe docs.python.org/reference/…

    – Ned Deily

    28. September 2009 um 8:06 Uhr

  • Das Unäre + Betreiber hat einen Nutzen. Bei decimal.Decimal-Objekten wird auf die aktuelle Genauigkeit gerundet.

    – u0b34a0f6ae

    28. September 2009 um 9:10 Uhr

  • Ich wette auf Parser-Vereinfachung. Notieren Sie sich einen Artikel in PEP 3099, “Things that will Not Change in Python 3000”: “Der Parser wird nicht komplexer sein als LL(1). Einfach ist besser als komplex. Diese Idee erstreckt sich auch auf den Parser. Die Grammatik von Python auf einen LL(1)-Parser beschränken ist ein Segen, kein Fluch. Es legt uns Handschellen an, die uns daran hindern, über Bord zu gehen und mit abgefahrenen Grammatikregeln zu enden, wie einige andere dynamische Sprachen, die unbenannt bleiben, wie Perl.“ Ich sehe nicht, wie man disambiguieren kann + + und ++ ohne LL(1) zu brechen.

    – Mike DeSimone

    14. Oktober 2010 um 19:42 Uhr

  • Es ist nicht richtig, das zu sagen ++ ist nichts weiter als ein Synonym für += 1. Es gibt Prä-Inkrement- und Post-Inkrement-Varianten von ++, also ist es eindeutig nicht dasselbe. Den restlichen Punkten stimme ich aber zu.

    – PhilHibbs

    17. Mai 2017 um 8:59 Uhr

Benutzer-Avatar
Lennart Regebro

Python hat keine Pre- und Post-Inkrement-Operatoren.

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

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

a und b oben sind eigentlich dasselbe Objekt. Wenn Sie a erhöhen würden, würden Sie auch b erhöhen. Das ist nicht das, was Sie wollen. Also musst du neu vergeben. So was:

b = b + 1

Viele C-Programmierer, die Python verwendet haben, wollten einen Inkrementoperator, aber dieser Operator würde so aussehen, als würde er das Objekt inkrementieren, während er es tatsächlich neu zuweist. Deshalb, die -= und += Operatoren, wo hinzugefügt, um kürzer als die zu sein b = b + 1während es klarer und flexibler ist als b++also erhöhen die meisten Leute mit:

b += 1

Welche neu zuweisen b zu b+1. Das ist kein Inkrementoperator, weil er nicht inkrementiert bes weist es neu zu.

Kurz gesagt: Python verhält sich hier anders, weil es nicht C ist und kein Low-Level-Wrapper um Maschinencode, sondern eine dynamische High-Level-Sprache, bei der Inkremente keinen Sinn machen und auch nicht so notwendig sind wie in C , wo Sie sie zum Beispiel jedes Mal verwenden, wenn Sie eine Schleife haben.

  • Dieses Beispiel ist falsch (und Sie verwechseln wahrscheinlich Unveränderlichkeit mit Identität) – sie haben dieselbe ID aufgrund einer VM-Optimierung, die dieselben Objekte für Zahlen bis 255 (oder so ähnlich) verwendet. Bsp (größere Zahlen): >>> a = 1231231231231 >>> b = 1231231231231 >>> id(a), id(b) (32171144, 32171168)

    – ionelmc

    11. Januar 2010 um 1:00 Uhr


  • Die Behauptung der Unveränderlichkeit ist falsch. Konzeptionell, i++ würde zuordnen bedeuten i + 1 zum Variable i. i = 5; i++ bedeutet zuordnen 6 zu inicht ändern int Objekt, auf das gezeigt wird i. Das heißt, es bedeutet nicht Erhöhen Sie den Wert von 5!

    – Mechanische Schnecke

    20. September 2011 um 4:19 Uhr


  • @Mechanische Schnecke: In diesem Fall wären es überhaupt keine Inkrementoperatoren. Und dann ist der Operator += klarer, expliziter, flexibler und macht sowieso dasselbe.

    – Lennart Regebro

    20. September 2011 um 5:35 Uhr

  • @LennartRegebro: In C++ und Java, i++ arbeitet nur mit lvalues. Wenn das Objekt, auf das gezeigt wird, erhöht werden soll iwäre diese Einschränkung unnötig.

    – Mechanische Schnecke

    20. September 2011 um 7:18 Uhr

  • Unveränderlichkeit ist in diesem Fall tatsächlich irrelevant. Bedenken Sie: Clojure hat einen eingebauten Inkrementoperator und alle Datenstrukturen sind standardmäßig unveränderlich. Es stimmt zwar, dass Sie einen neuen Verweis auf den neuen Wert erhalten, aber das ist meistens orthogonal zur rein syntaktischen Wahl von ++ vs. += 1.

    – Charles Parker

    30. Januar 2013 um 18:26 Uhr

Während die anderen Antworten insofern richtig sind, als sie zeigen, was für eine bloße + normalerweise tut (nämlich die Zahl so zu lassen, wie sie ist, wenn es eine ist), sind sie insofern unvollständig, als sie nicht erklären, was passiert.

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

Ich könnte mir eine SEHR seltsame Klassenstruktur (Kinder, macht das nicht zu Hause!) so vorstellen:

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)

Benutzer-Avatar
RBF06

TL;DR

Python hat keine unären Inkrement/Dekrement-Operatoren (--/++). Um einen Wert zu erhöhen, verwenden Sie stattdessen

a += 1

Mehr Details und Fallstricke

Aber seien Sie hier vorsichtig. Wenn Sie von C kommen, ist sogar dies in Python anders. Python hat keine “Variablen” im Sinne von C, stattdessen verwendet Python Namen und Objekteund in Python ints sind unveränderlich.

Sagen wir also, Sie tun es

a = 1

Was dies in Python bedeutet, ist: Erstellen Sie ein Objekt des Typs int Wert haben 1 und den Namen binden a dazu. Das Objekt ist eine Instanz von int Wert haben 1und die Name a verweist darauf. Der Name a und das Objekt, auf das es sich bezieht, sind verschieden.

Sagen wir jetzt, Sie tun es

a += 1

Seit ints unveränderlich sind, passiert hier Folgendes:

  1. Suchen Sie das Objekt, das a bezieht sich auf (es ist eine int mit Ausweis 0x559239eeb380)
  2. den Wert des Objekts nachschlagen 0x559239eeb380 (es ist 1)
  3. Addiere 1 zu diesem Wert (1 + 1 = 2)
  4. ein … kreieren Neu int Objekt mit Wert 2 (es hat die Objekt-ID 0x559239eeb3a0)
  5. den Namen neu binden a zu diesem neuen Objekt
  6. Jetzt a bezieht sich auf Objekt 0x559239eeb3a0 und das ursprüngliche Objekt (0x559239eeb380) wird nicht mehr mit dem Namen bezeichnet a. Wenn es keine anderen Namen gibt, die auf das ursprüngliche Objekt verweisen, wird es später von der Garbage Collection erfasst.

Probieren Sie es selbst aus:

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

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

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

Innerhalb einer Funktion müssen Sie locals() als zweites Argument hinzufügen, wenn Sie die lokale Variable ändern möchten, andernfalls wird versucht, die globale 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 Folgendes tun:

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

Aber meiner Meinung nach ist folgender Ansatz 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.

  • Hinweis: Diese Hilfsmethoden sind zwar großartig, funktionieren jedoch nicht, wenn Ihre Locals im Klassenfunktions-Stack-Frame vorhanden sind. dh – das Aufrufen innerhalb einer Klassenmethode def funktioniert nicht – das ‘locals()’-Diktat ist ein Schnappschuss und aktualisiert den Stack-Frame nicht.

    – Adam

    14. Juni 2019 um 5:49 Uhr

In Python 3.8+ können Sie Folgendes tun:

(a:=a+1) #same as ++a (increment, then return new value)
(a:=a+1)-1 #same as a++ (return the incremented value -1) (useless)

Damit kann man sich viele Gedanken machen.

>>> a = 0
>>> while (a:=a+1) < 5:
    print(a)

    
1
2
3
4

Oder wenn Sie etwas mit ausgefeilterer Syntax schreiben möchten (das Ziel ist nicht die Optimierung):

>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
    print(a)

    
1
2
3
4

Es gibt 0 zurück, auch wenn ‘a’ nicht fehlerfrei existiert, und setzt es dann auf 1

  • Hinweis: Diese Hilfsmethoden sind zwar großartig, funktionieren jedoch nicht, wenn Ihre Locals im Klassenfunktions-Stack-Frame vorhanden sind. dh – das Aufrufen innerhalb einer Klassenmethode def funktioniert nicht – das ‘locals()’-Diktat ist ein Schnappschuss und aktualisiert den Stack-Frame nicht.

    – Adam

    14. Juni 2019 um 5:49 Uhr

In Python wird im Gegensatz zu Sprachen wie Common Lisp, Scheme oder Ruby strikt zwischen Ausdrücken und Anweisungen unterschieden.

Wikipedia

Durch die Einführung solcher Operatoren würden Sie also die Trennung von Ausdruck und Anweisung aufheben.

Aus dem gleichen Grund kannst du nicht schreiben

if x = 0:
  y = 1

wie Sie es in einigen anderen Sprachen können, in denen eine solche Unterscheidung nicht beibehalten wird.

  • Interessanterweise wird diese Einschränkung in der kommenden Version Python 3.8 mit der neuen Syntax für Zuweisungsausdrücke (PEP-572 python.org/dev/peps/pep-0572). Wir werden schreiben können if (n := len(a)) > 10: y = n + 1 zum Beispiel. Beachten Sie, dass die Unterscheidung aufgrund der Einführung eines neuen Operators für diesen Zweck klar ist (:=)

    – Zertrin

    26. August 2019 um 9:34 Uhr


1146080cookie-checkVerhalten von Inkrement- und Dekrementoperatoren in Python

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy