2016-02-08 12 views
9

Nach der Lektüre this und this, die auf meine Frage ziemlich ähnlich sind, kann ich noch nicht das folgende Verhalten verstehen:Python3 Mehrfachbelegung und Speicheradresse

a = 257 
b = 257 
print(a is b) #False 
a, b = 257, 257 
print(a is b) #True 

Wenn id(a) und id(b) Druck ich, dass die Variablen sehen kann, zu denen wurden die Werte in getrennten Leitungen zugeordnet sind, unterschiedliche IDs haben, während bei beiden Werten Mehrfachzuordnung die gleiche ID haben:

a = 257 
b = 257 
print(id(a)) #139828809414512 
print(id(b)) #139828809414224 
a, b = 257, 257 
print(id(a)) #139828809414416 
print(id(b)) #139828809414416 

Aber es ist unmöglich, dieses Verhalten zu erklären, daß eine Mehrfachzuordnung von gleichen Werten, da immer Zeiger auf die gleiche ID erstellt:

a, b = -1000, -1000 
print(id(a)) #139828809414448 
print(id(b)) #139828809414288 

Gibt es eine klare Regel, was erklärt, wenn die Variablen die gleichen id und, wenn sie nicht bekommen?

bearbeiten

relevante Informationen: Der Code in dieser Frage im interaktiven Modus ausgeführt wurde (ipython3)

+0

Check out '[id (i) für in i (1000,1000,1000,1000)]' :-) –

+4

Hinweis: Das Verhalten ist inkonsistent, weil es nie ankommt. Wenn es einen Grund gäbe, sich darum zu kümmern, ob zwei '257's das gleiche Objekt waren, würde es ein einfaches, vernünftiges Muster geben. – user2357112

+0

Peephole-Optimierungen, irrelevant, wie es funktioniert, da es nie etwas ist, auf das Sie sich verlassen würden oder sollten. –

Antwort

2

Dies liegt an einer konstanten Faltungsoptimierung im Bytecode-Compiler. Wenn der Bytecode-Compiler einen Batch von Anweisungen kompiliert, behält er uses a dict, um die Konstanten zu verfolgen, die er sieht. Dieses Diktat führt automatisch äquivalente Konstanten zusammen.

Hier ist die Routine verantwortlich für die Aufzeichnung und Nummerierung Konstanten (sowie einige damit verbundenen Verantwortlichkeiten):

static int 
compiler_add_o(struct compiler *c, PyObject *dict, PyObject *o) 
{ 
    PyObject *t, *v; 
    Py_ssize_t arg; 

    t = _PyCode_ConstantKey(o); 
    if (t == NULL) 
     return -1; 

    v = PyDict_GetItem(dict, t); 
    if (!v) { 
     arg = PyDict_Size(dict); 
     v = PyInt_FromLong(arg); 
     if (!v) { 
      Py_DECREF(t); 
      return -1; 
     } 
     if (PyDict_SetItem(dict, t, v) < 0) { 
      Py_DECREF(t); 
      Py_DECREF(v); 
      return -1; 
     } 
     Py_DECREF(v); 
    } 
    else 
     arg = PyInt_AsLong(v); 
    Py_DECREF(t); 
    return arg; 
} 

Sie können sehen, dass es nur einen neuen Eintrag ergänzt und weist eine neue Nummer, wenn es nicht der Fall ist Finde eine äquivalente Konstante, die bereits vorhanden ist. (Das Bit _PyCode_ConstantKey stellt sicher, dass Dinge wie 0.0, -0.0 und 0 als nicht gleichwertig betrachtet werden.)

Im interaktiven Modus endet eine Charge jedes Mal, wenn der Dolmetscher muss tatsächlich Ihren Befehl ausführen, so konstantes Falten geschieht meist nicht über Befehle:

>>> a = 1000 
>>> b = 1000 
>>> a is b 
False 
>>> a = 1000; b = 1000 # 1 batch 
>>> a is b 
True 

In einem Skript alle Top-Level-Aussagen eine Charge ist, so more constant folding happens:

a = 257 
b = 257 
print a is b 

In einem Skript diese druckt True.

ein Code der Funktion erhält seine Konstanten getrennt von Code außerhalb der Funktion verfolgt, die Konstantenfaltung begrenzt:

a = 257 

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

f() 

Even in a script dieses druckt False.

+0

danke. Ich werde meine Frage bearbeiten, um zu verdeutlichen, dass sie im interaktiven Modus getestet wurde. – istern

+0

Was ist also mit Integer kleiner als '-5' in Python 3? Beispiel 'a, b = -6, -6' – Kasramvd

+0

@Kasramvd: Der Compiler kompiliert' -6' in einen '' LOAD_CONST' und einem UNARY_NEGATIVE'; gibt es eine [separate Optimierung] (https://hg.python.org/cpython/file/3.5/Python/peephole.c#l242) verantwortlich für die in einen 'LOAD_CONST' umgewandelt werden, die eine' -6' lädt. Diese Optimierung verwendet das Diktat nicht. – user2357112

0

Eine solche Regel Implementierung spezifisch ist. Beispielsweise weist CPython int Objekte für kleine ganze Zahlen (-5 bis 256) als Leistungsoptimierung vor.

Die einzige allgemeine Regel besteht darin, davon auszugehen, dass die Verwendung eines Literals ein neues Objekt erzeugt.

+1

Die Zahl ist '257' und Sie können dieses Verhalten auch mit größeren Ganzzahlen sehen. – Kasramvd

+0

Ich vergesse immer die genaue Reichweite. – chepner

+0

nicht "wird generiert", sondern "könnte generieren". – glglgl

2

Das ist wegen der Python-Interpreter-Optimierung bei UNPACK_SEQUENCE Zeit, während des Ladens der Const-Werte. Wenn Python während des Entpackens auf eine iterierbare Zahl trifft, lädt es die doppelten Objekte nicht mehrfach, sondern behält nur das erste Objekt und weist alle Ihre Namen einer Objektadresse zu. Somit wären alle Ihre Variablen die gleichen Referenzen auf ein Objekt.

Eigentlich wäre Ihr auspacken zu folgendem Befehl gleichwertig sein:

a = b = 257 

Und über die negativen Zahlen in Python 2.x es keinen Unterschied, aber in Python 3.X, dass es scheint, für die Zahlen nicht kleiner machen als -5 Python wird beim Entpacken ein neues Objekt erzeugen:

>>> a, b = -6, -6 
>>> a is b 
False 
>>> a, b = -5, -5 
>>> 
>>> a is b 
True 
+0

Wie erklärt Ihre Antwort den letzten Teil meiner Frage, mit a, b = -1000, -1000? – istern

+0

@isternberg Im letzten Teil hast du deine Variablen separat erstellt, aber es gibt einen weiteren Punkt in negativen Zahlen, die in python3 entpackt werden. Ich werde die Antwort so schnell wie möglich mit dem Grund aktualisieren. – Kasramvd

+0

„ein LOAD_CONSTANT und 2 STORE_FAST“ - Während Sie richtig sind, die 'LOAD_CONSTANT' ist eine Konstante' tuple' Laden, die hält 2 Artikel. Ich bin mir nicht sicher, ob es für mich klar ist, wie das erklärt, dass die beiden Werte die gleichen IDs haben (wenn sie außerhalb des normalen Bereichs der von Python internierten Ganzzahlen liegen). – mgilson

Verwandte Themen