Unerwartete Ausgabe mit dem ternären Operator von Python in Kombination mit Lambda

8

Ich habe eine spezifische Situation, in der ich folgendes tun möchte (eigentlich ist es mehr als das, aber ich habe das Problem auf das Wesentliche reduziert):

%Vor%

was eine schwierige Art zu schreiben ist:

%Vor%

aber in Wirklichkeit sind '1', 'True' und '2' zusätzliche Ausdrücke, die ausgewertet werden und die die Variable 'e' benötigen, die ich für dieses vereinfachte Codebeispiel auf '0' setze.

Beachten Sie den Unterschied in der Ausgabe von beiden obigen Ausdrücken, obwohl

%Vor%

Das Lustige ist, dass dies ein spezieller Fall ist, denn wenn ich "1" durch "3" ersetze, bekomme ich das erwartete / gewünschte Ergebnis:

%Vor%

Es ist sogar richtig, wenn ich '1' durch '0' (was auch ein Sonderfall sein könnte, da 1 == True und 0 == False)

ersetzt %Vor%

Auch wenn ich "True" durch "nicht False" oder "Not not True" ersetze, funktioniert es immer noch:

%Vor%

Eine andere alternative Formulierung verwendet die übliche Anweisung if..then..else und erzeugt keinen Fehler:

%Vor%

Was erklärt dieses Verhalten? Wie kann ich dieses Verhalten auf eine nette Art und Weise lösen (vermeiden Sie es, nicht 'nicht wahr' oder so etwas zu verwenden?

)

Danke!

    
tvo 05.12.2015, 02:56
quelle

1 Antwort

9

Ich denke, ich habe herausgefunden, warum der Bug passiert und warum Ihre Repro Python 3-spezifisch ist.

Code-Objekte führen Gleichheitsvergleiche nach Wert durch , anstatt durch Zeiger, Seltsamerweise:

%Vor%

In Python 2 sucht lambda e: True nach einem globalen Namen und lambda e: 1 lädt eine Konstante 1 , so dass die Codeobjekte für diese Funktionen nicht gleich sind. In Python 3 ist True ein Schlüsselwort und beide laddas laden Konstanten. Seit 1 == True sind die Codeobjekte ausreichend ähnlich, so dass alle Überprüfungen in code_richcompare bestanden werden, und die Codeobjekte vergleichen dasselbe. (Einer der Checks ist für die Zeilennummer, daher erscheint der Fehler nur, wenn sich die Lambdas in derselben Zeile befinden.)

Der Bytecode-Compiler ruft ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts) auf, um LOAD_CONST zu erstellen. Anweisung, die einen Lambda-Code auf den Stack lädt, und ADDOP_O verwendet ein dict, um die hinzugefügten Objekte zu verfolgen, um Platz zu sparen für Dinge wie doppelte Konstanten. Es hat eine gewisse Handhabung, um Dinge wie 0.0 , 0 und -0.0 zu unterscheiden, die ansonsten gleichwertig wären, aber es wurde nicht erwartet, dass sie jemals mit gleichwertigen, aber nicht gleichwertigen Code-Objekten umgehen müssten. Die Codeobjekte werden nicht richtig unterschieden, und die beiden Lambdas teilen sich schließlich ein einzelnes Codeobjekt.

Indem wir True durch 1.0 ersetzen, können wir den Fehler in Python 2 reproduzieren:

%Vor%

Ich habe Python 3.5 nicht, also kann ich nicht überprüfen, ob der Fehler in dieser Version noch vorhanden ist. Ich habe im Bug Tracker nichts über den Bug gesehen, aber ich hätte den Bericht knapp verpasst. Wenn der Fehler immer noch vorhanden ist und nicht gemeldet wurde, sollte dies gemeldet werden.

    
user2357112 11.12.2015, 07:26
quelle