Warum gibt es in Python keine ++ und - Operatoren?

442

Warum gibt es keine ++und --Operatoren in Python?

Leonid
quelle
weil dort überflüssig
cmarangu
Es gibt 4 verschiedene ++ - Operatoren, die alle dasselbe tun. Oh und da wird das "++" aus "C ++" entfernt, das scheint eine Degeneration zu sein
cmarangu

Antworten:

443

Es ist nicht, weil es keinen Sinn ergibt; Es ist durchaus sinnvoll, "x ++" als "x + = 1" zu definieren und die vorherige Bindung von x zu bewerten.

Wenn Sie den ursprünglichen Grund wissen möchten, müssen Sie entweder alte Python-Mailinglisten durchblättern oder jemanden fragen, der dort war (z. B. Guido), aber es ist leicht genug, dies nachträglich zu rechtfertigen:

Einfaches Inkrementieren und Dekrementieren ist nicht so erforderlich wie in anderen Sprachen. Sie schreiben nicht for(int i = 0; i < 10; ++i)sehr oft Dinge wie in Python; stattdessen machst du Dinge wie for i in range(0, 10).

Da es nicht annähernd so oft benötigt wird, gibt es viel weniger Gründe, ihm eine eigene spezielle Syntax zu geben. Wenn Sie inkrementieren müssen, +=ist in der Regel in Ordnung.

Es ist keine Entscheidung darüber, ob es sinnvoll ist oder ob es getan werden kann - es tut und es kann. Es ist eine Frage, ob der Nutzen es wert ist, zur Kernsyntax der Sprache hinzugefügt zu werden. Denken Sie daran, dies sind vier Operatoren - postinc, postdec, preinc, previous, und jeder dieser Operatoren müsste seine eigenen Klassenüberladungen haben. Sie müssen alle spezifiziert und getestet werden. es würde der Sprache Opcodes hinzufügen (was eine größere und daher langsamere VM-Engine impliziert); Jede Klasse, die ein logisches Inkrement unterstützt, müsste diese implementieren (zusätzlich zu +=und -=).

Dies ist alles überflüssig mit +=und -=, so würde es ein Nettoverlust werden.

Glenn Maynard
quelle
98
Es ist oft nützlich, so etwas wie Array [i ++] zu verwenden, was mit + = / - = nicht ordentlich gemacht wird.
Turner Hayes
102
@thayes: Das ist in Python kein gängiges Muster.
Glenn Maynard
9
@thayes Da das in einer Schleife sein wird, können Sie genauso gut idirekt eine Schleife durchlaufen - wenn Sie es tatsächlich brauchen und nicht einfach zB verwenden könnenarray.append()
Tobias Kienzler
31
Ich sehe das viel größere Problem in der Lesbarkeit und Vorhersehbarkeit. In meinen C-Tagen sah ich mehr als genug Fehler, die aus Missverständnissen über die Unterscheidung zwischen i++und ++i... stammten
Charles Duffy
5
Hinzu kommt die Rechtfertigung im Nachhinein: Bei einem Projekt, an dem ich arbeite, bin ich (mehr als jeder andere in seinem Leben) auf eine ganze Menge C ++ - Code gestoßen, der unter Problemen leidet ++und auf --eine Weise verwendet wird, die zu undefinierten oder nicht spezifizierten Ergebnissen führt Verhalten. Sie ermöglichen es, komplizierten, schwer korrekt zu analysierenden Code zu schreiben.
Brian Vandenberg
84

Diese ursprüngliche Antwort, die ich geschrieben habe, ist ein Mythos aus der Folklore des Rechnens : Entlarvt von Dennis Ritchie als "historisch unmöglich", wie in den Briefen an die Herausgeber der Kommunikation der ACM vom Juli 2012 doi: 10.1145 / 2209249.2209251 vermerkt


Die C-Inkrementierungs- / Dekrementierungsoperatoren wurden zu einer Zeit erfunden, als der C-Compiler nicht sehr intelligent war und die Autoren die direkte Absicht angeben wollten, einen Maschinensprachenoperator zu verwenden, der eine Handvoll Zyklen für einen Compiler sparte, der könnte ein tun

load memory
load 1
add
store memory

anstatt

inc memory 

und die PDP-11 noch unterstützt „Autoinkrement“ und „Autoinkrement latente“ Anweisungen entsprechend *++pund *p++, respectively. Siehe Abschnitt 5.3 des Handbuchs, wenn Sie schrecklich neugierig sind.

Da Compiler intelligent genug sind, um die in die Syntax von C integrierten allgemeinen Optimierungstricks zu verarbeiten, sind sie jetzt nur noch eine syntaktische Annehmlichkeit.

Python hat keine Tricks, um dem Assembler Absichten zu vermitteln, da es keine verwendet.

msw
quelle
4
Javascript hat ++. Ich denke nicht, dass dies ein "Trick ist, um dem Monteur Absichten zu vermitteln". Außerdem hat Python Bytecode. Ich denke, der Grund ist etwas anderes.
Nathan Davis
13
Dieses Geschäft, das dem Compiler Hinweise gibt, ist in der Tat ein Mythos. Ehrlich gesagt ist es eine blöde Ergänzung zu jeder Sprache und verstößt gegen die folgenden zwei Vorschriften: 1. Sie codieren nicht für den Computer, sondern für einen anderen Ingenieur. Und 2. Sie codieren nicht für einen kompetenten Ingenieur zum Lesen, sondern für einen kompetenten Ingenieur zum Lesen, während er um 3 Uhr morgens erschöpft ist und Koffein zu sich nimmt.
3
@ tgm1024 Um fair zu sein, wenn Sie einen Halbduplex-Teletyp mit 10 bis 30 Zeichen pro Sekunde codieren, codieren Sie, damit Sie ihn vor nächster Woche eingeben können.
Msw
4
@ tgm1024 Unix und C sahen den Großteil ihrer anfänglichen Entwicklung auf PDP-11s, die unglaublich langsame Teletypen für die Benutzerkommunikation verwendeten. Während du tot Recht, dass heute für die Maschine Codierung meist albern ist, damals war es die Mensch / Maschine - Schnittstelle , die der Engpass war. Es ist schwer vorstellbar, so langsam zu arbeiten, wenn Sie es nie mussten.
Msw
65

Ich habe immer angenommen, dass es mit dieser Linie des Python-Zen zu tun hat:

Es sollte einen - und vorzugsweise nur einen - offensichtlichen Weg geben, dies zu tun.

x ++ und x + = 1 machen genau das Gleiche, es gibt also keinen Grund, beides zu haben.

GSto
quelle
10
one--ist Null?
Andre Holzner
25
one--ist eins im Satz, aber unmittelbar danach null. Dieses 'Koan' deutet also auch darauf hin, dass Inkrementierungs- / Dekrementierungsoperatoren nicht offensichtlich sind.
Victor K
14
@EralpB Wenn Sie + = löschen, können Sie Dinge wie x + = 10 nicht tun. + = Ist ein allgemeinerer Fall von ++
Rory
8
Außerdem: "Explizit ist besser als implizit".
Ber
2
Auf jeden Fall nicht dasselbe, denn x + = 1 ist KEIN Ausdruck - es ist eine Aussage - und es wird nichts ausgewertet. Sie können nichts tun wie: 'row [col ++] = a; row [col ++] = b '. Ganz zu schweigen von den Pre-Inc- und Post-Inc-Dingen, die c ++ hat.
Uri
38

Natürlich könnten wir sagen "Guido hat sich gerade so entschieden", aber ich denke, die Frage betrifft wirklich die Gründe für diese Entscheidung. Ich denke, es gibt mehrere Gründe:

  • Es mischt Aussagen und Ausdrücke zusammen, was keine gute Praxis ist. Siehe http://norvig.com/python-iaq.html
  • Es ermutigt die Leute im Allgemeinen, weniger lesbaren Code zu schreiben
  • Zusätzliche Komplexität bei der Sprachimplementierung, die in Python, wie bereits erwähnt, nicht erforderlich ist
EMP
quelle
12
Ich bin froh, dass jemand endlich den Aspekt Aussage gegen Ausdruck erwähnt hat. In C ist die Zuweisung ein Ausdruck und damit der ++ - Operator. In Python ist die Zuweisung eine Anweisung. Wenn sie also ein ++ hätte, müsste sie wahrscheinlich auch eine Zuweisungsanweisung sein (und noch weniger nützlich oder erforderlich).
Martineau
Einverstanden - wenn es sich um Aussagen handelte, wäre es zumindest absolut bedeutungslos, über den Unterschied zwischen Post- und Pre-Operatoren zu sprechen.
Crowman
17

Weil in Python Ganzzahlen unveränderlich sind (int's + = gibt tatsächlich ein anderes Objekt zurück).

Außerdem müssen Sie sich mit ++ / - Gedanken über das Inkrementieren / Dekrementieren vor und nach dem Inkrementieren machen, und es ist nur noch ein Tastendruck erforderlich, um zu schreiben x+=1. Mit anderen Worten, es vermeidet mögliche Verwirrung auf Kosten eines sehr geringen Gewinns.

Nathan Davis
quelle
Ints sind auch in C unveränderlich. Wenn Sie dies nicht glauben, versuchen Sie, Ihren C-Compiler dazu zu bringen, Code für 42++... zu generieren. So etwas (Ändern einer Literalkonstante) war in einigen alten Fortran-Compilern tatsächlich möglich (oder wie ich gelesen habe): Alle zukünftigen Verwendungen von diesem Literal in diesem Programmlauf hätte dann wirklich einen anderen Wert. Viel Spaß beim Debuggen!
Lutz Prechelt
10
Recht. 42 ist eine wörtliche Konstante . Konstanten sind (oder sollten zumindest unveränderlich sein). Das bedeutet nicht, dass intCs im Allgemeinen unveränderlich sind. Ein intin C bezeichnet einfach einen Ort im Speicher. Und die Teile an diesem Ort sind sehr veränderlich. Sie können beispielsweise eine Referenz von erstellen intund die Referenz dieser Referenz ändern. Diese Änderung ist in allen Verweisen (einschließlich der ursprünglichen intVariablen) auf diesen Ort sichtbar . Das Gleiche gilt nicht für ein Python-Integer-Objekt.
Nathan Davis
x + = 1 wird nicht zu inc mem. Python führt einen Funktionsaufruf aus, um einer Variablen 1 hinzuzufügen. es ist erbärmlich.
PalaDolphin
12

Klarheit!

Bei Python geht es viel um Klarheit, und kein Programmierer kann die Bedeutung wahrscheinlich richtig erraten, es --asei denn, er hat eine Sprache mit diesem Konstrukt gelernt.

Bei Python geht es auch viel darum , Konstrukte zu vermeiden, die zu Fehlern führen, und die ++Operatoren sind bekanntermaßen reich an Fehlern. Diese beiden Gründe reichen aus, um diese Operatoren nicht in Python zu haben.

Die Entscheidung, dass Python Einrückungen zum Markieren von Blöcken anstelle von syntaktischen Mitteln wie einer Form von Anfangs- / Endklammerung oder obligatorischer Endmarkierung verwendet, basiert weitgehend auf denselben Überlegungen.

Schauen Sie sich zur Veranschaulichung die Diskussion um die Einführung eines bedingten Operators (in C :) cond ? resultif : resultelsein Python im Jahr 2005 an. Lesen Sie mindestens die erste Nachricht und die Entscheidungsnachricht dieser Diskussion (die zuvor mehrere Vorläufer zum gleichen Thema hatte).

Wissenswertes: Das darin häufig erwähnte PEP ist das "Python Extension Proposal" PEP 308 . LC bedeutet Listenverständnis , GE bedeutet Generatorausdruck (und keine Sorge, wenn diese Sie verwirren, sind sie keine der wenigen komplizierten Stellen von Python).

Lutz Prechelt
quelle
10

Ich verstehe, warum Python keinen ++Operator hat: Wenn Sie dies in Python schreiben, erhalten a=b=c=1Sie drei Variablen (Beschriftungen), die auf dasselbe Objekt zeigen (welcher Wert 1 ist). Sie können dies überprüfen, indem Sie die ID-Funktion verwenden, die eine Objektspeicheradresse zurückgibt:

In [19]: id(a)
Out[19]: 34019256

In [20]: id(b)
Out[20]: 34019256

In [21]: id(c)
Out[21]: 34019256

Alle drei Variablen (Beschriftungen) zeigen auf dasselbe Objekt. Erhöhen Sie nun eine der Variablen und sehen Sie, wie sie sich auf die Speicheradressen auswirkt:

In [22] a = a + 1

In [23]: id(a)
Out[23]: 34019232

In [24]: id(b)
Out[24]: 34019256

In [25]: id(c)
Out[25]: 34019256

Sie können sehen, dass diese Variable ajetzt als Variablen bund auf ein anderes Objekt zeigt c. Weil Sie es verwendet a = a + 1haben, ist es ausdrücklich klar. Mit anderen Worten, Sie weisen der Beschriftung ein völlig anderes Objekt zu a. Stellen Sie sich vor, Sie können schreiben, a++dies würde darauf hindeuten, dass Sie der Variablen kein aneues Objekt zugewiesen haben, sondern das alte Objekt inkrementeller erhöht haben. All dieses Zeug ist meiner Meinung nach zur Minimierung von Verwirrung. Zum besseren Verständnis sehen Sie, wie Python-Variablen funktionieren:

Warum kann eine Funktion in Python einige Argumente ändern, die vom Aufrufer wahrgenommen werden, andere jedoch nicht?

Ist Python Call-by-Value oder Call-by-Reference? Weder.

Übergibt Python nach Wert oder als Referenz?

Ist Python als Referenz oder als Wert übergeben?

Python: Wie übergebe ich eine Variable als Referenz?

Grundlegendes zu Python-Variablen und Speicherverwaltung

Emulieren des Pass-by-Value-Verhaltens in Python

Python-Funktionen werden als Referenz aufgerufen

Code wie ein Pythonista: Idiomatisches Python

Wakan Tanka
quelle
9

Es wurde nur so entworfen. Inkrementierungs- und Dekrementierungsoperatoren sind nur Verknüpfungen für x = x + 1. Python hat normalerweise eine Entwurfsstrategie angewendet, die die Anzahl alternativer Mittel zum Ausführen einer Operation reduziert. Die erweiterte Zuweisung kommt den Inkrementierungs- / Dekrementierungsoperatoren in Python am nächsten und wurde erst in Python 2.0 hinzugefügt.

Reed Copsey
quelle
3
Ja Kumpel, wie könnten Sie ersetzen return a[i++]mit return a[i=i+1].
Luís de Sousa
7

Ich bin sehr neu in Python, aber ich vermute, der Grund liegt in der Betonung zwischen veränderlichen und unveränderlichen Objekten innerhalb der Sprache. Jetzt weiß ich, dass x ++ leicht als x = x + 1 interpretiert werden kann, aber es sieht so aus, als würden Sie ein Objekt an Ort und Stelle inkrementieren, das unveränderlich sein könnte.

Nur meine Vermutung / Gefühl / Ahnung.

mkoistinen
quelle
In dieser Hinsicht x++ist näher an x += 1als an x = x + 1, diese beiden machen auch bei veränderlichen Objekten einen Unterschied.
glglgl
4

Erstens wird Python nur indirekt von C beeinflusst; Es wird stark von ABC beeinflusst , das diese Operatoren anscheinend nicht hat. Daher sollte es keine große Überraschung sein, sie auch nicht in Python zu finden.

Zweitens werden, wie andere gesagt haben, Inkrementieren und Dekrementieren von +=und -=bereits unterstützt.

Drittens umfasst die vollständige Unterstützung eines ++und eines --Operator-Sets normalerweise die Unterstützung sowohl der Präfix- als auch der Postfix-Version. In C und C ++ kann dies zu allen Arten von "schönen" Konstrukten führen, die (für mich) gegen den Geist der Einfachheit und Geradlinigkeit zu sein scheinen, den Python umfasst.

Während die C-Anweisung while(*t++ = *s++);für einen erfahrenen Programmierer einfach und elegant erscheint, ist sie für jemanden, der sie lernt, alles andere als einfach. Wenn Sie eine Mischung aus Präfix- und Postfix-Inkrementen und -Dekrementen verwenden, müssen selbst viele Profis innehalten und ein wenig nachdenken.

wberry
quelle
3

Ich glaube, es ergibt sich aus dem Python-Credo, dass "explizit besser ist als implizit".

Sepheus
quelle
Nun, Sie schreiben nicht explizit "begin" - und "end" -Anweisungen in Python, oder? Obwohl ich der Aussage zustimme, denke ich, dass dem Grenzen gesetzt sind. Während wir über diese Grenzen streiten können, können wir uns alle einig sein, dass es eine Linie gibt, deren Überschreitung unpraktisch ist. Und da es zu dieser Entscheidung so viele Meinungen und Rechtfertigungen gibt, denke ich nicht, dass es eine klare Entscheidung war. Zumindest kann ich keine Quelle finden, in der es ausdrücklich angegeben ist
Hasan Ammori
3

Dies mag daran liegen, dass @GlennMaynard die Angelegenheit im Vergleich zu anderen Sprachen betrachtet, aber in Python tun Sie die Dinge auf Python-Weise. Es ist keine "Warum" -Frage. Es ist da und Sie können Dinge mit dem gleichen Effekt tun x+=. Im Zen von Python heißt es: "Es sollte nur einen Weg geben, ein Problem zu lösen." Mehrfachauswahl ist in der Kunst großartig (Meinungsfreiheit), aber in der Technik mies.

Nihal Sahu
quelle
2

Die ++Klasse der Operatoren sind Ausdrücke mit Nebenwirkungen. Dies ist etwas, das in Python im Allgemeinen nicht zu finden ist.

Aus dem gleichen Grund ist eine Zuweisung in Python kein Ausdruck, wodurch die allgemeine if (a = f(...)) { /* using a here */ }Redewendung verhindert wird.

Schließlich vermute ich, dass dort Operatoren nicht sehr konsistent mit Pythons Referenzsemantik sind. Denken Sie daran, dass Python keine Variablen (oder Zeiger) mit der aus C / C ++ bekannten Semantik hat.

Ber
quelle
1
Nichts hindert daran, eine Funktion mit einem Nebeneffekt in einem Test- / Ausdrucks- / Listenverständnis aufzurufen: f(a)Wo aist eine Liste, ein unveränderliches Objekt.
Jean-François Fabre
1

Vielleicht wäre eine bessere Frage zu fragen, warum diese Operatoren in C existieren. K & R nennt Inkrementierungs- und Dekrementierungsoperatoren "ungewöhnlich" (Abschnitt 2.8, Seite 46). Die Einleitung nennt sie "prägnanter und oft effizienter". Ich vermute, dass die Tatsache, dass diese Operationen immer bei der Zeigermanipulation auftreten, auch bei ihrer Einführung eine Rolle gespielt hat. In Python wurde wahrscheinlich entschieden, dass es keinen Sinn macht, zu versuchen, Inkremente zu optimieren (tatsächlich habe ich gerade einen Test in C durchgeführt, und es scheint, dass die von gcc generierte Assembly in beiden Fällen addl anstelle von include verwendet), und es gibt keine Zeigerarithmetik; Es wäre also nur eine weitere Möglichkeit gewesen, und wir wissen, dass Python das verabscheut.

Ludovico Fischer
quelle
1

so wie ich es verstanden habe, wirst du nicht denken, dass der Wert im Speicher geändert wird. Wenn Sie in c x ++ ausführen, ändert sich der Wert von x im Speicher. aber in Python sind alle Zahlen unveränderlich, daher hat die Adresse, auf die x zeigte, immer noch x, nicht x + 1. Wenn Sie x ++ schreiben, denken Sie, dass x ändern, was wirklich passiert, ist, dass x-Aktualisierung in einen Speicherort geändert wird, in dem x + 1 gespeichert ist, oder erstellen Sie diesen Speicherort neu, wenn doe nicht vorhanden ist.

rafi wiener
quelle
1
Was ++unterscheidet das also von += 1?
Ber
1

Um bereits gute Antworten auf dieser Seite zu vervollständigen:

Nehmen wir an, wir entscheiden uns dafür, prefix ( ++i), das die unären + und - Operatoren brechen würde.

Heutzutage wird ein Präfix von ++oder --nichts ausgeführt, da der unäre Plus-Operator zweimal (nichts) oder der unäre minus zweimal (zweimal: bricht sich selbst ab) aktiviert wird.

>>> i=12
>>> ++i
12
>>> --i
12

Das würde diese Logik möglicherweise brechen.

Jean-François Fabre
quelle
1

Andere Antworten haben beschrieben, warum es für Iteratoren nicht benötigt wird, aber manchmal ist es nützlich, wenn Sie eine Variable inline zuweisen, um sie zu erhöhen. Sie können den gleichen Effekt mit Tupeln und Mehrfachzuweisungen erzielen:

b = ++a wird:

a,b = (a+1,)*2

und b = a++wird:

a,b = a+1, a

3.8 Python stellt die Zuordnung :=Betreiber, so dass wir erreichen foo(++a)mit

foo(a:=a+1)

foo(a++) ist aber immer noch schwer fassbar.

JeffUK
quelle
2
: = Zuweisung ist eine Schande
nehem
0

Ich denke, dies bezieht sich auf die Konzepte der Veränderlichkeit und Unveränderlichkeit von Objekten. 2,3,4,5 sind in Python unveränderlich. Siehe das Bild unten. 2 hat die ID bis zu diesem Python-Prozess festgelegt.

ID von Konstanten und Variablen

x ++ würde im Wesentlichen ein In-Place-Inkrement wie C bedeuten. In C führt x ++ In-Place-Inkremente durch. X = 3 und x ++ würden also 3 im Speicher auf 4 erhöhen, im Gegensatz zu Python, wo 3 noch im Speicher vorhanden wäre.

Daher müssen Sie in Python keinen Wert im Speicher neu erstellen. Dies kann zu Leistungsoptimierungen führen.

Dies ist eine auf Ahnung basierende Antwort.

Pikesh Prasoon
quelle
0

Ich weiß, dass dies ein alter Thread ist, aber der häufigste Anwendungsfall für ++ i wird nicht behandelt, nämlich das manuelle Indizieren von Sets, wenn keine Indizes bereitgestellt werden. In dieser Situation bietet Python enumerate ()

Beispiel: Wenn Sie in einer bestimmten Sprache ein Konstrukt wie foreach verwenden, um über eine Menge zu iterieren, sagen wir für das Beispiel sogar, dass es sich um eine ungeordnete Menge handelt, und Sie benötigen einen eindeutigen Index für alles, um sie voneinander zu unterscheiden

i = 0
stuff = {'a': 'b', 'c': 'd', 'e': 'f'}
uniquestuff = {}
for key, val in stuff.items() :
  uniquestuff[key] = '{0}{1}'.format(val, i)
  i += 1

In solchen Fällen bietet Python eine Aufzählungsmethode, z

for i, (key, val) in enumerate(stuff.items()) :
Jessica Pennell
quelle