Wie verwende ich Prä-Inkrement/Dekrement-Operatoren (++
, --
), genau wie in C++?
Warum tut ++count
ausführen, aber den Wert der Variablen nicht ändern?
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 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:
++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.++
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.++
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
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 + 1
wä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 b
es 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 i
nicht ä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 i
wä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)
RBF06
Python hat keine unären Inkrement/Dekrement-Operatoren (--
/++
). Um einen Wert zu erhöhen, verwenden Sie stattdessen
a += 1
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 int
s 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 1
und 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 int
s unveränderlich sind, passiert hier Folgendes:
a
bezieht sich auf (es ist eine int
mit Ausweis 0x559239eeb380
)0x559239eeb380
(es ist 1
)int
Objekt mit Wert 2
(es hat die Objekt-ID 0x559239eeb3a0
)a
zu diesem neuen Objekta
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.
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
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 diea
Akku wox
zeigt, dann inkrementiertx
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