46 Stimmen

Wie kann eine nicht zugewiesene Zeichenfolge in Python eine Adresse im Speicher haben?

Kann mir das jemand erklären? Ich habe also mit dem id()-Befehl in Python gespielt und bin auf das hier gestoßen:

>>> id('cat')
5181152
>>> a = 'cat'
>>> b = 'cat'
>>> id(a)
5181152
>>> id(b)
5181152

Das ergibt für mich einen gewissen Sinn, bis auf einen Teil: Die Zeichenkette "cat" hat eine Adresse im Speicher, bevor ich sie einer Variablen zuweise. Wahrscheinlich verstehe ich einfach nicht, wie Speicheradressierung funktioniert, aber kann mir das jemand erklären oder mir zumindest sagen, dass ich mich über Speicheradressierung informieren sollte?

Das ist ja alles schön und gut, aber das hat mich noch mehr verwirrt:

>>> a = a[0:2]+'t'
>>> a
'cat'
>>> id(a)
39964224
>>> id('cat')
5181152

Das kam mir seltsam vor, weil 'Katze' ist eine Zeichenkette mit der Adresse 5181152, aber die neue a hat eine andere Adresse. Wenn es also zwei 'Katze' Strings im Speicher, warum werden nicht zwei Adressen für id('Katze') ? Mein letzter Gedanke war, dass die Verkettung etwas mit der Änderung der Adresse zu tun hat, also habe ich Folgendes versucht:

>>> id(b[0:2]+'t')
39921024
>>> b = b[0:2]+'t'
>>> b
'cat'
>>> id(b)
40000896

Ich hätte erwartet, dass die IDs identisch sind, aber das war nicht der Fall. Was denken Sie?

53voto

kindall Punkte 167554

Python verwendet Zeichenkettenliterale ziemlich aggressiv wieder. Die Regeln, nach denen dies geschieht, sind implementierungsabhängig, aber CPython verwendet zwei, die mir bekannt sind:

  • Zeichenketten, die nur Zeichen enthalten, die in Python-Bezeichnern zulässig sind, sind interniert, Das heißt, sie werden in einer großen Tabelle gespeichert und überall, wo sie vorkommen, wiederverwendet. Egal, wo Sie also "cat" verweist sie immer auf dasselbe String-Objekt.
  • String-Literale im selben Codeblock werden unabhängig von ihrem Inhalt und ihrer Länge wiederverwendet. Wenn Sie ein Stringliteral der gesamten Gettysburg Address zweimal in eine Funktion einfügen, handelt es sich beide Male um das gleiche Stringobjekt. In getrennten Funktionen handelt es sich um unterschiedliche Objekte: def foo(): return "pack my box with five dozen liquor jugs" def bar(): return "pack my box with five dozen liquor jugs" assert foo() is bar() # AssertionError

Beide Optimierungen werden zur Kompilierzeit durchgeführt (d.h. wenn der Bytecode erzeugt wird).

Auf der anderen Seite, etwas wie chr(99) + chr(97) + chr(116) ist eine Zeichenkette Ausdruck die die Zeichenkette auswertet "cat" . In einer dynamischen Sprache wie Python kann ihr Wert zur Kompilierzeit nicht bekannt sein ( chr() ist eine eingebaute Funktion, aber Sie könnten sie neu zugewiesen haben), so dass sie normalerweise nicht interniert wird. Daher ist ihre id() unterscheidet sich von dem der "cat" . Sie können jedoch erzwingen, dass eine Zeichenkette interniert wird, indem Sie die intern() Funktion. So:

id(intern(chr(99) + chr(97) + chr(116))) == id("cat")   # True

Wie bereits von anderen erwähnt, ist das Internieren möglich, weil Zeichenketten unveränderlich sind. Es ist nicht möglich, zu ändern "cat" à "dog" mit anderen Worten. Sie müssen ein neues String-Objekt erzeugen, d. h. es besteht keine Gefahr, dass andere Namen, die auf denselben String verweisen, davon betroffen sind.

Nebenbei bemerkt: Python konvertiert auch Ausdrücke, die nur Konstanten enthalten (wie "c" + "a" + "t" ) zur Kompilierzeit in Konstanten umzuwandeln, wie die folgende Disassemblierung zeigt. Diese werden so optimiert, dass sie auf identische String-Objekte gemäß den obigen Regeln verweisen.

>>> def foo(): "c" + "a" + "t"
...
>>> from dis import dis; dis(foo)
  1           0 LOAD_CONST               5 ('cat')
              3 POP_TOP
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE

48voto

David Heffernan Punkte 585606

'cat' hat eine Adresse, weil Sie sie erstellen, um sie an id() . Sie haben es noch nicht mit einem Namen verknüpft, aber das Objekt existiert noch.

Python zwischenspeichert und kurze Zeichenketten wiederverwendet. Wenn Sie jedoch Zeichenketten durch Verkettung zusammensetzen, wird der Code, der den Cache durchsucht und die Wiederverwendung versucht, umgangen.

Beachten Sie, dass das Innenleben des String-Cache ein reines Implementierungsdetail ist, auf das Sie sich nicht verlassen sollten.

17voto

Ned Batchelder Punkte 342778

Alle Werte müssen sich irgendwo im Speicher befinden. Aus diesem Grund id('cat') ergibt einen Wert. Sie nennen sie eine "nicht existierende" Zeichenfolge, aber sie existiert eindeutig, sie wurde nur noch keinem Namen zugewiesen.

Strings sind unveränderlich, so dass der Interpreter clevere Dinge tun kann, wie z.B. alle Instanzen des Literal 'cat' das gleiche Objekt sein, so dass id(a) y id(b) sind identisch.

Die Bearbeitung von Zeichenketten erzeugt neue Zeichenketten. Dabei kann es sich um dieselben Zeichenketten wie bei früheren Zeichenketten mit demselben Inhalt handeln, muss es aber nicht.

Beachten Sie, dass es sich bei all diesen Details um Implementierungsdetails von CPython handelt, und dass sie sich jederzeit ändern können. In aktuellen Programmen müssen Sie sich nicht mit diesen Fragen beschäftigen.

8voto

Python-Variablen unterscheiden sich stark von Variablen in anderen Sprachen (z. B. C).

In vielen anderen Sprachen ist eine Variable ein Name für einen Speicherplatz. In diesen Sprachen können sich verschiedene Arten von Variablen auf verschiedene Arten von Speicherplätzen beziehen, und ein und demselben Speicherplatz können mehrere Namen gegeben werden. In den meisten Fällen können sich die Daten an einem bestimmten Speicherplatz von Zeit zu Zeit ändern. Es gibt auch Möglichkeiten, sich indirekt auf Speicherplätze zu beziehen ( int *p würde die Adresse enthalten, und an der Speicherstelle an dieser Adresse befindet sich eine Ganzzahl). Aber die tatsächliche Stelle, auf die eine Variable verweist, kann sich nicht ändern; die Variable ist den Standort. Eine Variablenzuweisung in diesen Sprachen bedeutet im Grunde genommen: "Suchen Sie den Speicherort für diese Variable und kopieren Sie diese Daten dorthin".

Python funktioniert nicht auf diese Weise. In Python werden tatsächliche Objekte an einem Speicherplatz abgelegt, und Variablen sind wie Markierungen für Speicherplätze. Python verwaltet die gespeicherten Werte auf eine andere Weise als die Variablen. Im Wesentlichen bedeutet eine Zuweisung in Python: "Schauen Sie in den Informationen für diese Variable nach, vergessen Sie den Ort, auf den sie sich bereits bezieht, und ersetzen Sie ihn durch den neuen Ort". Es werden keine Daten kopiert.

Ein gemeinsames Merkmal von Sprachen, die wie Python funktionieren (im Gegensatz zu der ersten Art, über die wir vorhin gesprochen haben), ist, dass einige Arten von Objekten auf besondere Weise verwaltet werden; identische Werte werden zwischengespeichert, damit sie keinen zusätzlichen Speicherplatz benötigen und damit sie sehr einfach verglichen werden können (wenn sie die gleiche Adresse haben, sind sie gleich). Dieser Prozess wird als Praktikum Alle Python-String-Literale sind interniert (zusätzlich zu einigen anderen Typen), obwohl dynamisch erzeugte Strings dies nicht sein können.

In Ihrem exakten Code würde der semantische Dialog lauten:

# before anything, since 'cat' is a literal constant, add it to the intern cache
>>> id('cat') # grab the constant 'cat' from the intern cache and look up 
              # it's address
5181152
>>> a = 'cat' # grab the constant 'cat' from the intern cache and 
              # make the variable "a" point to it's location 
>>> b = 'cat' # do the same thing with the variable "b"
>>> id(a) # look up the object "a" currently points to, 
          # then look up that object's address
5181152
>>> id(b) # look up the object "b" currently points to, 
          # then look up that object's address
5181152

1voto

Heath Hunnicutt Punkte 17871

Der von Ihnen gepostete Code erzeugt neue Zeichenketten als Zwischenobjekte. Diese erstellten Zeichenfolgen haben schließlich denselben Inhalt wie Ihre Originale. In der Zwischenzeit stimmen sie nicht genau mit dem Original überein und müssen unter einer anderen Adresse gespeichert werden.

>>> id('cat')
5181152

Wie andere bereits geantwortet haben, veranlassen Sie die Python-VM durch diese Anweisungen, ein String-Objekt zu erstellen, das die Zeichenkette "cat" enthält. Dieses String-Objekt wird zwischengespeichert und befindet sich an der Adresse 5181152.

>>> a = 'cat'
>>> id(a)
5181152

Auch hier wurde a zugewiesen, um auf dieses zwischengespeicherte String-Objekt bei 5181152 zu verweisen, das "cat" enthält.

>>> a = a[0:2]
>>> id(a)
27731511

An diesem Punkt in meiner geänderten Version Ihres Programms haben Sie zwei kleine String-Objekte erstellt: 'cat' y 'ca' . 'cat' noch im Cache vorhanden ist. Die Zeichenfolge, auf die a verweist, ist ein anderes und wahrscheinlich neues String-Objekt, das die Zeichen 'ca' .

>>> a = a + 't'
>>> id(a)
39964224

Nun haben Sie ein weiteres neues String-Objekt erstellt. Dieses Objekt ist die Verkettung der Zeichenkette 'ca' an der Adresse 27731511, und die Zeichenfolge 't' . Diese Verkettung stimmt mit der zuvor zwischengespeicherten Zeichenfolge überein 'cat' . Python erkennt diesen Fall nicht automatisch. Wie kindall angedeutet hat, können Sie die Suche mit der Option intern() méthode.

Wir hoffen, dass diese Erklärung die Schritte verdeutlicht, mit denen die Adresse der a geändert.

Ihr Code enthielt nicht den Zwischenzustand mit a die Zeichenfolge 'ca' . Die Antwort gilt immer noch, da der Python-Interpreter ein neues String-Objekt erzeugt, das das Zwischenergebnis enthält a[0:2] unabhängig davon, ob Sie das Zwischenergebnis einer Variablen zuweisen oder nicht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X