Jeder, der lange genug an Python herumgebastelt hat, wurde von dem folgenden Problem gebissen (oder in Stücke gerissen):
def foo(a=[]):
a.append(5)
return a
Python-Neulinge würden erwarten, dass diese Funktion immer eine Liste mit nur einem Element zurückgibt: [5]
. Das Ergebnis ist jedoch ganz anders und (für einen Anfänger) sehr erstaunlich:
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
Ein Manager von mir hatte einmal seine erste Begegnung mit dieser Funktion und nannte sie einen "dramatischen Designfehler" der Sprache. Ich entgegnete ihm, dass es für dieses Verhalten eine Erklärung gibt, und dass es in der Tat sehr verwirrend und unerwartet ist, wenn man die Interna nicht versteht. Allerdings konnte ich mir die folgende Frage nicht beantworten: Warum wird das Standardargument bei der Funktionsdefinition und nicht bei der Funktionsausführung gebunden? Ich bezweifle, dass das erfahrene Verhalten einen praktischen Nutzen hat (wer hat wirklich statische Variablen in C verwendet, ohne Bugs zu erzeugen?)
Editar :
Baczek gibt ein interessantes Beispiel . Zusammen mit den meisten Ihrer Kommentare und Utaal's im Besonderen habe ich weiter ausgearbeitet:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
Ich habe den Eindruck, dass die Design-Entscheidung sich darauf bezog, wo der Bereich der Parameter platziert werden sollte: innerhalb der Funktion oder "zusammen" mit ihr?
Wenn die Bindung innerhalb der Funktion erfolgt, würde dies bedeuten, dass x
ist beim Aufruf der Funktion effektiv an die angegebene Vorgabe gebunden und nicht definiert, was einen schwerwiegenden Fehler darstellen würde: Die def
Zeile wäre "hybrid" in dem Sinne, dass ein Teil der Bindung (des Funktionsobjekts) bei der Definition und ein Teil (Zuweisung von Standardparametern) beim Funktionsaufruf erfolgen würde.
Das tatsächliche Verhalten ist konsistenter: alles in dieser Zeile wird ausgewertet, wenn diese Zeile ausgeführt wird, also bei der Funktionsdefinition.
81 Stimmen
Ergänzende Frage - Gute Verwendungsmöglichkeiten für veränderbare Standardargumente
9 Stimmen
Ich habe keinen Zweifel daran, dass veränderbare Argumente das Prinzip des geringsten Erstaunens für eine durchschnittliche Person verletzen, und ich habe gesehen, wie Anfänger dort hineingetreten sind und dann heldenhaft Mailinglisten durch Mailing-Tupel ersetzt haben. Nichtsdestotrotz sind veränderbare Argumente immer noch im Einklang mit Python Zen (Pep 20) und fallen unter die Klausel "obvious for Dutch" (verstanden/ausgenutzt von Hardcore-Python-Programmierern). Der empfohlene Workaround mit doc string ist der beste, aber der Widerstand gegen doc strings und jegliche (geschriebene) Doku ist heutzutage nicht mehr so ungewöhnlich. Ich persönlich würde einen Dekorator bevorzugen (sagen wir @fixed_defaults).
6 Stimmen
Mein Argument, wenn ich darauf stoße, ist: "Warum müssen Sie eine Funktion erstellen, die eine Variable zurückgibt, die optional eine Variable sein kann, die Sie an die Funktion übergeben würden? Entweder wird eine Mutable geändert oder eine neue erstellt. Warum muss man beides mit einer Funktion machen? Und warum sollte der Interpreter so umgeschrieben werden, dass man das tun kann, ohne drei Zeilen zum Code hinzuzufügen?" Weil es hier darum geht, die Art und Weise, wie der Interpreter mit Funktionsdefinitionen und Evokationen umgeht, neu zu schreiben. Das ist eine Menge Arbeit für einen kaum notwendigen Anwendungsfall.
32 Stimmen
"Python-Neulinge würden erwarten, dass diese Funktion immer eine Liste mit nur einem Element zurückgibt:
[5]
." Ich bin ein Python-Neuling und würde das nicht erwarten, denn offensichtlichfoo([1])
wird zurückgegeben[1, 5]
ではなく[5]
. Was Sie sagen wollten, ist, dass ein Anfänger erwarten würde, dass die Funktion ohne Parameter aufgerufen wird immer zurückkehren[5]
.0 Stimmen
Für Beispiel in Python-Tutorial , warum ist
if L is None:
benötigt? Ich habe diesen Test entfernt und er hat keinen Unterschied gemacht0 Stimmen
@sdaffa23fdsf Was wäre, wenn
L
ist nichtNone
Sie möchten den eingehenden Wert dieses Parameters nicht überschreiben.2 Stimmen
Das Problem sind veränderbare Vorgaben. Die Antwort liegt also auf der Hand: nur unveränderliche Standardwerte zulassen . Dies würde die gängigsten Fälle, nämlich Literale und None, nicht verändern und einige andere wie Tupel und eingefrorene Mengen weiterhin zulassen.
7 Stimmen
Diese Frage lautet "Warum wurde das [der falsche Weg] so umgesetzt?" Sie fragt nicht nach "Was ist der richtige Weg?" die von [ abgedeckt wird. Warum behebt arg=None das Problem der veränderlichen Standardargumente in Python? ]*( stackoverflow.com/questions/10676729/ ). Neue Nutzer sind fast immer weniger an ersterem und viel mehr an letzterem interessiert, so dass es manchmal sehr nützlich ist, diesen Link/Doppelpunkt anzuführen.
1 Stimmen
Als Python-Neuling seit etwa zwei Wochen fällt mir auf, dass die offizielle Anleitung enthält eine Warnung vor diesem - Wichtige Warnung: Der Standardwert wird nur einmal ausgewertet. Dies macht einen Unterschied, wenn der Standardwert ein veränderbares Objekt wie eine Liste, ein Wörterbuch oder Instanzen der meisten Klassen ist.
1 Stimmen
Diese Frage hat sich in der Praxis als strittig und meinungsgesteuert (und nicht faktenbasiert) erwiesen und erzeugt "mehr Hitze als Licht", genau die Art und Weise, die wir hier zu vermeiden versuchen.
1 Stimmen
Ich bin anderer Meinung. Es gibt einige sehr informative Informationen darüber, warum Python so entworfen wurde, wie es war. Dies hilft zu klären, wie nicht nur dieses Muster problematisch sein kann, sondern wie andere veränderbare Standardparameter sich verhalten.
2 Stimmen
Ich kann mir nicht helfen, aber ich denke, dass die Antwort in der Bearbeitung für X argumentiert, indem sie X annimmt. In diesem Fall argumentiert sie, dass es in Ordnung ist, das Prinzip der geringsten Verwunderung (POLA) hier zu verletzen, weil es Ihnen erlaubt, Objekte inline als Standardparameter zu instanziieren, etwas, das IMHO auch gegen POLA verstößt. Wenn hingegen nur Literale und Objektreferenzen als Standardparameter erlaubt wären, würde dies POLA erfüllen. y Ich denke, es wäre einfacher, auf eine Weise zu implementieren, die für den durchschnittlichen Python-Entwickler Sinn macht. Ja Sie kann die seltsame Art und Weise, wie Dinge gemacht werden, zu lernen, aber das sollten Sie nicht müssen. POLA.
0 Stimmen
Wenn ich von Literalen spreche, meine ich Zeichenketten, Zahlen, Objekte und Listen, wobei die beiden letzteren nur aus Zeichenketten und Zahlen bestehen, oder Objekte und Listen, die nur aus... nun, Sie verstehen schon. Und es würde dann eine tiefe Kopie des Standardparameters bei jedem Aufruf erstellen und ihn übergeben, damit es keine Überschneidungen zwischen den Aufrufen gibt.
0 Stimmen
Wenn man weiß, was veränderbare Objekte und unveränderliche Objekte sind, kann man dieses Verhalten erklären. Es ist das beste Beispiel, um einem Neuling wie mir das Verhalten von veränderlichen und nicht veränderlichen Objekten beizubringen!
1 Stimmen
Je mehr ich Python benutze, desto mehr schätze ich Scala und bin dankbar, dass es es gibt. Python ist eine Abscheulichkeit. Davon abgesehen hat es seinen Platz. Natürlich hat dieser Kommentar nicht viel mit Ihrer Frage zu tun.
1 Stimmen
Das Einzige, was ich erstaunlich finde, ist, dass jemand einen Parameter verändert. Wenn man das tut, sollte man wirklich das Unerwartete erwarten.
2 Stimmen
Das Problem ist eindeutig, dass
[]
ist syntaktischer Zucker für einen Funktionsaufruf, der einen Wert zurückgibt. Für den Anfänger sieht es aus wie "das Standardargument ist eine neue leere Liste", nicht wie "das Standardargument ist ein Verweis auf eine bestimmte, zunächst leere Liste". Der "Sprachfehler", den ich hier sehe, ist, dass Python so konzipiert ist, dass man viel länger als in anderen Sprachen ohne Referenzen auskommen kann, und wenn sie dann auftauchen, ist es erschreckend und beunruhigend. Normalerweise wird dies jedoch als Merkmal betrachtet.0 Stimmen
@JonKiparsky Eh? Du hättest genau das gleiche Problem mit
list()
anstelle von[]
.0 Stimmen
@wjandrea Ich bin mir nicht sicher, welches Problem Sie als "genau dasselbe" bezeichnen. Ich spreche von einem Problem der Benutzererwartung, nicht von einem Problem des Sprachverhaltens. Ich könnte mich irren, aber ich denke, dass der Benutzer nicht die gleichen Erwartungen an die Sprache hat.
list()
wie sie es für[]
- Ich stimme zu, dass die Einstellunglist()
als Parameter für eine Funktion hätte genau das gleiche Problem wie die Verwendung von[]
Ich bin mir aber nicht sicher, ob die Nutzer erwarten, dass Ersteres funktioniert, während sie eindeutig erwarten, dass Letzteres funktioniert.0 Stimmen
Derzeit sind die einzigen beiden Antworten, die zugeben, dass dies ein Designfehler in Python ist: stackoverflow.com/a/1139730/247696 y stackoverflow.com/a/71674471/247696
0 Stimmen
@Flimm, was ein "Konstruktionsfehler" ist oder nicht, ist subjektiv, wie die Tatsache zeigt, dass die erste Antwort widerlegt ausdrücklich diese Bewertung und entwickelt ein Gespür dafür, warum das Verhalten zu erwarten ist. Es gibt auch plausible Verwendungszwecke dafür (obwohl es bessere Werkzeuge für diese Zwecke gibt). Auf jeden Fall soll es bei Stack Overflow nicht um solche Einschätzungen gehen: siehe stackoverflow.com/help/dont-ask .