Dies ist mein zweiter Tag, an dem ich Python lerne (ich kenne die Grundlagen von C ++ und etwas OOP), und ich habe einige leichte Verwirrung hinsichtlich der Variablen in Python.
So verstehe ich sie derzeit:
Python-Variablen sind Verweise (oder Zeiger?) Auf Objekte (die entweder veränderlich oder unveränderlich sind). Wenn wir so etwas haben num = 5
, wird das unveränderliche Objekt 5
irgendwo im Speicher erstellt, und das Name-Objekt-Referenzpaar num
wird in einem bestimmten Namespace erstellt. Wenn wir haben a = num
, wird nichts kopiert, aber jetzt beziehen sich beide Variablen auf dasselbe Objekt und werden a
demselben Namespace hinzugefügt.
Hier verwirrt mich mein Buch " Automatisiere das langweilige Zeug mit Python" . Da es sich um ein Neuling-Buch handelt, werden keine Objekte, Namespaces usw. erwähnt, und es wird versucht, den folgenden Code zu erklären:
>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42
Die Erklärung, die es bietet, ist genau die gleiche wie die eines C ++ - Buches, über das ich mich nicht freue, da es sich um Verweise / Zeiger auf Objekte handelt. In diesem Fall wird in der dritten Zeile, da Ganzzahlen unveränderlich spam
sind, ein völlig neuer Zeiger / Verweis auf eine andere Stelle im Speicher zugewiesen, dh der Speicher, auf den er ursprünglich zeigte, wurde nicht geändert. Daher cheese
beziehen wir uns auf das ursprüngliche Objekt, auf das von verwiesen wird spam
. Ist das die richtige Erklärung?
42
inspam
, nun Sie gespeichertspam
in Käse Mittelcheese = 42
, Danach Sie ersetztspam
zu100
, Du hast nicht bearbeitetcheese
, deshalb istcheese
nach wie vor ist42
.spam
auf die Nummer 42 geklebt. Als nächstes haben Sie das Etikettcheese
auf das als gekennzeichnete Objekt geklebtspam
( wohlgemerkt nicht auf das Etikett selbst). Dann haben Sie dasspam
Etikett abgezogen und auf die Nummer 100 gesetzt.Antworten:
Als C ++ - Entwickler können Sie sich Python-Variablen als Zeiger vorstellen.
Wenn Sie also schreiben
spam = 100
, bedeutet dies, dass Sie "den Zeiger zuweisen", der zuvor auf das Objekt42
zeigte, um auf das Objekt zu zeigen100
.Früher
cheese
wurde zugewiesen, auf dasselbe Objektspam
zu zeigen, auf das gezeigt wurde, was sich42
zu diesem Zeitpunkt befand. Da Sie nicht geändert habencheese
, zeigt es immer noch auf42
.Unveränderlichkeit hat in diesem Fall nichts damit zu tun, da die Zeigerzuweisung nichts an dem Objekt ändert, auf das gezeigt wird.
quelle
So wie ich das sehe, gibt es unterschiedliche Ansichten einer Sprache.
Aus der Sicht des Sprachrechtsanwalts "zeigen" Python-Variablen immer auf ein Objekt. Im Gegensatz zu Java und C ++ hängt das Verhalten von == <=> = etc jedoch vom Laufzeittyp der Objekte ab, auf die die Variablen zeigen. Darüber hinaus wird in Python die Speicherverwaltung von der Sprache übernommen.
Aus der Perspektive eines praktischen Programmierers können wir die Tatsache, dass Ganzzahlen, Zeichenfolgen, Tupel usw. unveränderliche * Objekte und keine geraden Werte sind, als irrelevantes Detail behandeln. Die Ausnahme ist, dass beim Speichern großer Mengen numerischer Daten möglicherweise Typen verwendet werden sollen, die die Werte direkt speichern können (z. B. numpy-Arrays), anstatt Typen, die ein Array voller Verweise auf winzige Objekte erhalten.
Aus Sicht der Implementierer haben die meisten Sprachen eine Art Als-ob-Regel, sodass die Implementierung korrekt ist, wenn die angegebenen Verhaltensweisen korrekt sind, unabhängig davon, wie die Dinge tatsächlich unter der Haube ausgeführt werden.
Ja, Ihre Erklärung ist aus Sicht eines Sprachrechtsanwalts richtig. Ihr Buch ist aus Sicht eines praktischen Programmierers korrekt. Was eine Implementierung tatsächlich tut, hängt von der Implementierung ab. In cpython sind Ganzzahlen echte Objekte, obwohl Ganzzahlen mit kleinen Werten aus einem Cache-Pool entnommen und nicht neu erstellt werden. Ich bin mir nicht sicher, was die anderen Implementierungen (z. B. Pypy und Jython) tun.
* Beachten Sie hier die Unterscheidung zwischen veränderlichen und unveränderlichen Objekten. Bei einem veränderlichen Objekt müssen wir vorsichtig sein, es "wie einen Wert" zu behandeln, da ein anderer Code es mutieren könnte. Mit einem unveränderlichen Objekt haben wir keine derartigen Bedenken.
quelle
Es ist richtig, dass Sie mehr oder weniger Variablen als Zeiger verwenden können. Beispielcode würde jedoch sehr hilfreich sein, um zu erklären, wie dies tatsächlich funktioniert.
Erstens werden wir die
id
Funktion stark nutzen :Es ist wahrscheinlich, dass dies unterschiedliche absolute Werte auf Ihrem Computer zurückgibt.
Betrachten Sie dieses Beispiel:
>>> foo = 'a string' >>> id(foo) 4565302640 >>> bar = 'a different string' >>> id(bar) 4565321816 >>> bar = foo >>> id(bar) == id(foo) True >>> id(bar) 4565302640
Sie können sehen, dass:
Wenn wir den Wert von foo ändern, wird er einer anderen ID zugewiesen:
>>> foo = 42 >>> id(foo) 4561661488 >>> foo = 'oh no' >>> id(foo) 4565257832
Eine interessante Beobachtung ist auch, dass Ganzzahlen implizit diese Funktionalität bis zu 256 haben:
>>> a = 100 >>> b = 100 >>> c = 100 >>> id(a) == id(b) == id(c) True
Ab 256 ist dies jedoch nicht mehr der Fall:
>>> a = 256 >>> b = 256 >>> id(a) == id(b) True >>> a = 257 >>> b = 257 >>> id(a) == id(b) False
Wenn Sie jedoch zuweisen
a
,b
bleibt die ID wie zuvor gezeigt unverändert:>>> a = b >>> id(a) == id(b) True
quelle
Python ist weder Referenzübergabe noch Wertübergabe. Python-Variablen sind keine Zeiger, sie sind keine Referenzen, sie sind keine Werte. Python-Variablen sind Namen .
Stellen Sie sich das als "Pass-by-Alias" vor, wenn Sie denselben Phrasentyp oder möglicherweise "Pass-by-Object" benötigen, da Sie dasselbe Objekt aus jeder Variablen mutieren können, die es angibt, wenn es veränderbar ist, aber neu zugewiesen wird Eine Variable (Alias) ändert nur diese eine Variable.
Der Name einer Python-Variablen ist ein Schlüssel im globalen (oder lokalen) Namespace, der praktisch ein Wörterbuch ist. Der zugrunde liegende Wert ist ein Objekt im Speicher. Die Zuweisung gibt diesem Objekt einen Namen. Die Zuweisung einer Variablen zu einer anderen Variablen bedeutet, dass beide Variablen Namen für dasselbe Objekt sind. Durch die Neuzuweisung einer Variablen wird geändert, welches Objekt von dieser Variablen benannt wird, ohne dass die andere Variable geändert wird. Sie haben das Tag verschoben, aber das vorherige Objekt oder andere Tags darauf nicht geändert.
Im zugrunde liegenden C-Code der CPython-Implementierung ist jedes Python-Objekt ein
PyObject*
, sodass Sie sich vorstellen können, dass es wie C funktioniert, wenn Sie nur Zeiger auf Daten hatten (keine Zeiger auf Zeiger, keine direkt übergebenen Werte).quelle
Wenn Sie
spam = 100
Python ausführen, erstellen Sie ein weiteres Objekt im Speicher, ändern Sie jedoch nicht das vorhandene. Sie haben also immer noch einen Zeigercheese
auf 42 undspam
auf 100quelle
In der
spam = 100
Zeile wird der vorherige Wert (Zeiger auf ein Objekt vom Typint
durch einen Wert42
) durch einen anderen Zeiger auf ein anderes Objekt (Typint
, Wert100
) ersetzt.quelle
new Class()
Syntax in C ++ erstellen . Darüber hinaus ist in Python alles eine Instanz vonobject
Klasse / Unterklasse.Wie @DeepSpace in den Kommentaren erwähnt hat, leistet Ned Batchelder hervorragende Arbeit bei der Entmystifizierung von Variablen (Namen) und Zuweisungen zu Werten in einem Blog, aus dem er auf der PyCon 2015 einen Vortrag über Fakten und Mythen zu Python-Namen und -Werten hielt . Es kann für Pythonisten auf jeder Ebene der Meisterschaft aufschlussreich sein.
quelle
In Python enthält eine Variable den Verweis auf das Objekt . Ein Objekt ist ein Teil des zugewiesenen Speichers, der einen Wert und einen Header enthält . Der Header des Objekts enthält seinen Typ und einen Referenzzähler, der angibt, wie oft auf dieses Objekt im Quellcode verwiesen wird, damit die Garbage Collection erkennen kann, ob ein Objekt erfasst werden kann.
Wenn Sie nun einer Variablen Werte zuweisen, weist Python tatsächlich Referenzen zu, die Zeiger auf Speicherorte sind, die Objekten zugewiesen sind:
# x holds a reference to the memory location allocated for # the object(type=string, value="Hello World", refCounter=1) x = "Hello World"
Wenn Sie nun derselben Variablen Objekte unterschiedlichen Typs zuweisen, ändern Sie die Referenz tatsächlich so, dass sie auf ein anderes Objekt (dh einen anderen Speicherort) verweist. Wenn Sie einer Variablen eine andere Referenz (und damit ein anderes Objekt) zuweisen, fordert der Garbage Collector den dem vorherigen Objekt zugewiesenen Speicherplatz sofort zurück, sofern keine andere Variable im Quellcode darauf verweist:
# x holds a reference to the memory location allocated for # the object(type=string, value="Hello World", refCounter=1) x = "Hello World" # Now x holds the reference to a different object(type=int, value=10, refCounter=1) # and object(type=string, value="Hello World", refCounter=0) -which is not refereced elsewhere # will now be garbage-collected. x = 10
Kommen wir jetzt zu Ihrem Beispiel:
spam
enthält den Verweis auf das Objekt (Typ = int, Wert = 42, refCounter = 1):>>> spam = 42
cheese
Enthält jetzt auch den Verweis auf das Objekt (Typ = int, Wert = 42, refCounter = 2)>>> cheese = spam
Jetzt enthält Spam einen Verweis auf ein anderes Objekt (Typ = int, Wert = 100, refCounter = 1).
>>> spam = 100 >>> spam 100
Käse zeigt jedoch weiterhin auf das Objekt (Typ = int, Wert = 42, refCounter = 1).
>>> cheese 42
quelle
Beim Speichern
spam = 42
wird ein Objekt im Speicher erstellt. Dann weisen Siecheese = spam
, Sie ordnet das Objekt referenziert durchspam
zucheese
. Und schließlichspam = 100
ändert sich beim Ändern nur dasspam
Objekt. Alsocheese = 42
.quelle
spam
zucheese
. Es werden keine neuen Objekte erstellt.Die Funktionsseite numpy.copy () enthält eine Erklärung
https://docs.scipy.org/doc/numpy/reference/generated/numpy.copy.html
Das Beispiel lautet wie folgt:
Erstellen Sie ein Array x mit einer Referenz y und einer Kopie z:
x = np.array([1, 2, 3]) y = x z = np.copy(x)
Beachten Sie, dass sich beim Ändern von x y ändert, nicht jedoch z:
x[0] = 10 x[0] == y[0] True x[0] == z[0] False
quelle