2014-12-12 8 views
18

Gestern stieß ich auf diesen seltsamen Entpackungsunterschied zwischen Python 2 und Python 3 und schien nach einer schnellen Google-Suche keine Erklärung zu finden.Was ist mit dieser Änderung des Entpackungsverhaltens von Python2 zu Python3?

Python 2.7.8

a = 257 
b = 257 
a is b # False 

a, b = 257, 257 
a is b # False 

Python 3.4.2

a = 257 
b = 257 
a is b # False 

a, b = 257, 257 
a is b # True 

Ich weiß es wahrscheinlich nicht die Richtigkeit eines Programms nicht beeinflusst, aber es hat Fehler mich ein wenig . Kann jemand etwas über diesen Unterschied beim Auspacken erzählen?

+4

ist es wirklich egal, man sollte sich nicht auf irgendein Internierungsverhalten verlassen ... Vergleich verwenden. –

+4

Python Praktikanten Zahlen in den gleichen Ausdruck, ich werde den entsprechenden Code finden (hatte es schon für eine frühere Frage :)) – filmor

+0

Dies könnte mit Ihrer Implementierung zu tun haben, und wie es kleine ganze Zahlen zwischenspeichert. Hast du es mit einer kleineren Nummer als 257 probiert? War die Wahl von 257 zufällig? –

Antwort

24

Dieses Verhalten ist zumindest teilweise damit zu tun, wie der Interpreter faltende Konstante und wie die REPL Code ausführt.

Denken Sie daran, dass CPython zuerst Code kompiliert (zu AST und dann Bytecode). Es wertet dann den Bytecode aus. Während der Kompilierung sucht das Skript nach Objekten, die unveränderlich sind, und speichert sie zwischen. Es dedupliziert sie auch . Also, wenn es

a = 257 
b = 257 

sieht, wird es a und b gegen das gleiche Objekt speichern:

import dis 

def f(): 
    a = 257 
    b = 257 

dis.dis(f) 
#>>> 4   0 LOAD_CONST    1 (257) 
#>>>    3 STORE_FAST    0 (a) 
#>>> 
#>>> 5   6 LOAD_CONST    1 (257) 
#>>>    9 STORE_FAST    1 (b) 
#>>>    12 LOAD_CONST    0 (None) 
#>>>    15 RETURN_VALUE 

Notiere die LOAD_CONST 1. Der 1 ist der Index in co_consts:

f.__code__.co_consts 
#>>> (None, 257) 

So diese beiden die gleiche 257 laden. Warum tritt dies nicht auf mit:

$ python2 
Python 2.7.8 (default, Sep 24 2014, 18:26:21) 
>>> a = 257 
>>> b = 257 
>>> a is b 
False 

$ python3 
Python 3.4.2 (default, Oct 8 2014, 13:44:52) 
>>> a = 257 
>>> b = 257 
>>> a is b 
False 

?

Jede Zeile ist in diesem Fall eine separate Kompilierungseinheit und die Deduplizierung kann nicht über sie hinweg erfolgen. Es funktioniert ähnlich wie

Als solche haben diese Codeobjekte beide einzigartige konstante Caches. Dies bedeutet, dass, wenn wir den Zeilenumbruch zu entfernen, wird die isTrue zurück:

>>> a = 257; b = 257 
>>> a is b 
True 

Tat dies der Fall für beide Python-Versionen ist.In der Tat ist dies genau, warum

>>> a, b = 257, 257 
>>> a is b 
True 

kehrt True als auch, Es ist nicht wegen irgendeiner Eigenschaft des Auspackens; Sie nur in der gleichen Compilierungseinheit platziert werden.

Dies gibt False für Versionen zurück, die nicht richtig falten; filmor links to Ideone, die diesen Fehler bei 2.7.3 und 3.2.3 zeigt. Auf diesen Versionen, die ihre Produkte mit den anderen Konstanten erstellt Tupel nicht teilen:

import dis 

def f(): 
    a, b = 257, 257 
    print(a is b) 

print(f.__code__.co_consts) 
#>>> (None, 257, (257, 257)) 

n = f.__code__.co_consts[1] 
n1 = f.__code__.co_consts[2][0] 
n2 = f.__code__.co_consts[2][1] 

print(id(n), id(n1), id(n2)) 
#>>> (148384292, 148384304, 148384496) 

Wieder aber ist es hier nicht um eine Änderung, wie die Objekte werden ausgepackt; es ist nur eine Änderung, wie die Objekte in co_consts gespeichert werden.

+0

Wow, vielen Dank, Veedrac! Ich habe wirklich nicht mit einer kleinen Verrücktheit gerechnet, die mich dazu bringt, zu solch einem detaillierten Einblick in die Funktionsweise des Python-Interpreters zu kommen. – Tiensbakung

8

Ich denke, das ist eigentlich zufällig, da ich das Verhalten mit Python 3.2 nicht reproduzieren kann.

Es ist dieses Thema http://bugs.python.org/issue11244, die ein CONST_STACK zu beheben Probleme mit konstanten Tupeln mit negativen Zahlen führt nicht optimiert (siehe die Patches gegen peephole.c, die optimiser Python enthält läuft).

Dies scheint auch zu dem gegebenen Verhalten geführt zu haben. Immer noch in das schauen :)

+0

Hmm, interessant! Ich habe den Code in einem Skript getestet, sowohl Python 2 als auch Python 3 haben für alle Fälle True zurückgegeben. Rate nur der interaktive Interpreter ist ein bisschen faul. Wie auch immer, ich werde es als eine kleine Optimierung von Python 3 über Python 2 akzeptieren, bevor ich zu paranoid werde :) – Tiensbakung

+2

Ich habe gerade 'a, b = 257,257' in Pythons 2.5, 2.6, 2.7, 3.3, 3.4 versucht. Jeder von ihnen meldete "a ist b" als wahr im interaktiven Interpreter. Sind Sie sich Ihrer ursprünglichen Experimente sicher? –

+0

Hmm, vielleicht macht IPython hier etwas Besonderes? Ein Unterschied zwischen dem interaktiven und dem Skript besteht darin, dass der erstere 'LOAD_CONST 257; LOAD_CONST 257 ', während in letzterem die Datei auf' LOAD_CONST (257, 257) 'gefaltet ist. – filmor

Verwandte Themen