Was ist die interne Implementierung von Listen und Tupeln in Python?

Frage 1: Die interne Implementierung von Listen und Tupeln

In Bezug auf die interne Implementierung von Listen und Tupeln möchte ich wissen, ob es sich um eine verknüpfte Liste oder ein Array handelt oder um eine Möglichkeit, das Array zu verknüpfen.

Die spezifische Struktur der Liste ist unten aufgeführt: Wie
Fügen Sie hier eine Bildbeschreibung ein
Sie sehen können, handelt es sich bei der Liste im Wesentlichen um ein überbelegtes Array. Unter diesen ist ob_item eine Liste von Zeigern, und jeder Zeiger darin zeigt auf ein Element der Liste. Und zugewiesen speichert den für diese Liste zugewiesenen Speicherplatz.

Es ist zu beachten, dass der Unterschied zwischen der zugewiesenen und der tatsächlichen Speicherplatzgröße der Liste. Die tatsächliche Speicherplatzgröße der Liste bezieht sich auf das von len (Liste) zurückgegebene Ergebnis, dh ob_size im obigen Codekommentar, das angibt, wie viele Elemente in der Liste gespeichert sind. In der Praxis ist der für die Liste zugewiesene vorab zugewiesene Speicherplatz häufig größer als ob_size, um die Speicherstruktur zu optimieren und die Notwendigkeit zu vermeiden, den Speicher jedes Mal neu zuzuweisen, wenn ein Element hinzugefügt wird (Einzelheiten finden Sie im Beispiel im Text).

Daher lautet ihre Beziehung: zugewiesen> = len (Liste) = ob_size.

Wenn der für die aktuelle Liste zugewiesene Speicherplatz voll ist (dh == len (Liste) zugewiesen), fordert das System mehr Speicherplatz an und kopiert alle Originalelemente. Die Größe des Speicherplatzes, der jedes Mal zugewiesen wird, wenn die Liste dem folgenden Muster folgt:


0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...

In ähnlicher Weise ist das Folgende die spezifische Struktur eines Tupels: Wie
Fügen Sie hier eine Bildbeschreibung ein
Sie sehen können, ähnelt es einer Liste und ist im Wesentlichen ein Array, aber die Größe des Raums ist fest. Im Gegensatz zum allgemeinen Array hat Python-Tupel viele Optimierungen vorgenommen, um die Effizienz des Programms zu verbessern.

Wenn die Größe des Tupels beispielsweise 20 nicht überschreitet, speichert Python es in einer internen freien Liste zwischen. Auf diese Weise kann Python direkt aus dem Cache geladen werden, wenn Sie in Zukunft dasselbe Tupel erstellen müssen, was die Effizienz des Programms verbessert.

Frage 2: Warum werden Elemente in der alten Hash-Tabelle immer spärlicher?

Wir können uns zuerst das schematische Diagramm der alten Hash-Tabelle ansehen:


--+-------------------------------+
  | 哈希值 (hash)(key)(value)
--+-------------------------------+
0 |    hash0      key0    value0
--+-------------------------------+
1 |    hash1      key1    value1
--+-------------------------------+
2 |    hash2      key2    value2
--+-------------------------------+
. |           ...
__+_______________________________+

Sie werden feststellen, dass es sich um ein überbelegtes Array handelt, das auf dem Hashwert des Elementschlüssels (Schlüssels) basiert, um den Index der Position zu berechnen, an der es eingefügt werden soll.

Angenommen, ich habe ein Wörterbuch wie das folgende:


{
    
    'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'}

Dann wird das Wörterbuch in einer Form gespeichert, die der folgenden ähnelt:


entries = [
['--', '--', '--']
[-230273521, 'dob', '1999-01-01'],
['--', '--', '--'],
['--', '--', '--'],
[1231236123, 'name', 'mike'],
['--', '--', '--'],
[9371539127, 'gender', 'male']
]

Das '-' bedeutet hier, dass sich an dieser Position kein Element befindet, aber Speicher zugewiesen wurde.

Wir wissen, dass, wenn der verbleibende Speicherplatz der Hash-Tabelle weniger als 1/3 beträgt, mehr Speicher neu zugewiesen wird, um die Effizienz verwandter Operationen sicherzustellen und Hash-Konflikte zu vermeiden. Wenn die Hash-Tabelle mehr und mehr Elemente enthält, gibt es daher immer mehr Speicherorte, an denen Speicher zugewiesen wird, die jedoch keine Elemente enthalten. Infolgedessen wird die Hash-Tabelle immer spärlicher.

Die Struktur der neuen Hash-Tabelle ändert dies und verbessert die Speicherplatznutzung erheblich. Die Struktur der neuen Hash-Tabelle ist wie folgt:


Indices
----------------------------------------------------
None | index | None | None | index | None | index ...
----------------------------------------------------


Entries
--------------------
hash0   key0  value0
---------------------
hash1   key1  value1
---------------------
hash2   key2  value2
---------------------
        ...
---------------------

Wie Sie sehen können, wird die Speicherstruktur in zwei Arrays unterteilt: Indikatoren und Einträge. "Keine" bedeutet, dass an dieser Stelle Speicher zugewiesen wird, jedoch keine Elemente vorhanden sind.

Wir verwenden auch das obige Beispiel. Der Speichermodus in der neuen Hash-Tabelle lautet wie folgt:


indices = [None, 1, None, None, 0, None, 2]
entries = [
[1231236123, 'name', 'mike'],
[-230273521, 'dob', '1999-01-01'],
[9371539127, 'gender', 'male']
]

Unter diesen entspricht der Wert des Elements in Indikatoren dem entsprechenden Index in Einträgen. Zum Beispiel entspricht die 1 in Indizes den Einträgen [1], was "dob" ist: "1999-01-01".

Im Gegensatz dazu werden wir deutlich spüren, dass die Speicherplatznutzung in der neuen Hash-Tabelle im Vergleich zur alten Hash-Tabelle erheblich verbessert ist.

Frage 3: Bedenken hinsichtlich Anomalien

Wenn Sie der Ausnahme im Ausnahmeblock der Ausnahmebehandlung eine Variable zuweisen, wird diese Variable gelöscht, wenn die Ausführung des Ausnahmeblocks endet. Dies entspricht dem folgenden Ausdruck:


e = 1
try:
    1 / 0
except ZeroDivisionError as e:
    try:
        pass
    finally:
        del e

Das e zeigt hier zunächst auf die Ganzzahl 1, wird jedoch am Ende des Ausnahmeblocks (del e) gelöscht, sodass die Programmausführung eine "NameError" -Ausnahme auslöst.

Daher möchten wir Sie daran erinnern, dass beim Schreiben von Code in normalen Zeiten sichergestellt werden muss, dass die von der Ausnahme in der Ausnahme zugewiesenen Variablen in nachfolgenden Anweisungen nicht mehr verwendet werden.

Frage 4: Über die Modifikation von Polymorphismus und globalen Variablen

  • Python bestimmt, ob der Polymorphismus des Typs und der von der Unterklasse geerbte Polymorphismus gleich sind?
  • Sie können + = nicht direkt zum Ändern globaler Variablen innerhalb einer Funktion verwenden, aber zum Auflisten globaler Variablen können Sie Anhängen, Erweitern usw. zum Ändern verwenden. Warum?

Bei der ersten Frage müssen wir das Konzept des Polymorphismus klarstellen, was bedeutet, dass es viele verschiedene Formen gibt. Daher sind der Polymorphismus des Beurteilungstyps und der von den Unterklassen geerbte Polymorphismus im Wesentlichen gleich, aber Sie können sie als zwei verschiedene Manifestationen des Polymorphismus verstehen.

Schauen wir uns noch einmal die zweite Frage an. Wenn das Objekt, auf das eine globale Variable zeigt, unveränderlich ist, z. B. eine Ganzzahl, eine Zeichenfolge usw., und wenn Sie versuchen, seinen Wert innerhalb einer Funktion zu ändern, ohne das Schlüsselwort global hinzuzufügen, wird eine Ausnahme ausgelöst:


x = 1

def func():
    x += 1
func()
x

## 输出
UnboundLocalError: local variable 'x' referenced before assignment

Dies liegt daran, dass das x in der Standardfunktion des Programms eine lokale Variable ist und Sie es direkt in Anführungszeichen setzen, ohne einen Wert zuzuweisen, was offensichtlich nicht möglich ist.

Wenn das Objekt, auf das die globale Variable zeigt, jedoch veränderbar ist, z. B. eine Liste, ein Wörterbuch usw., können Sie es innerhalb der Funktion ändern:


x = [1]

def func():
    x.append(2)
func()
x

## 输出
[1, 2]

Natürlich sollte beachtet werden, dass x.append (2) hier die Variable x nicht ändert und x immer noch auf die ursprüngliche Liste zeigt. Tatsächlich bedeutet dieser Satz, die Liste zu besuchen, auf die x zeigt, und 2 am Ende dieser Liste hinzuzufügen.

Ich denke du magst

Origin blog.csdn.net/qq_41485273/article/details/114138480
Empfohlen
Rangfolge