Was genau ist __weakref__ in Python?

70

Überraschenderweise gibt es keine explizite Dokumentation für __weakref__. Schwache Referenzen werden hier erklärt . __weakref__wird auch kurz in der Dokumentation von erwähnt __slots__. Aber ich konnte nichts über sich __weakref__selbst finden.

Was genau ist __weakref__? - Ist es nur ein Mitglied, das als Flagge fungiert: Wenn vorhanden, kann das Objekt schwach referenziert sein? - Oder ist es eine Funktion / Variable, die überschrieben / zugewiesen werden kann, um ein gewünschtes Verhalten zu erhalten? Wie?

Michael
quelle

Antworten:

57

__weakref__ist nur ein undurchsichtiges Objekt, das auf alle schwachen Verweise auf das aktuelle Objekt verweist. Tatsächlich ist es eine Instanz von weakref(oder manchmal weakproxy), die sowohl eine schwache Referenz auf das Objekt als auch Teil einer doppelt verknüpften Liste aller schwachen Referenzen für dieses Objekt ist.

Es ist nur ein Implementierungsdetail, mit dem der Garbage Collector schwache Referenzen darüber informieren kann, dass sein Referent erfasst wurde, und keinen Zugriff mehr auf den zugrunde liegenden Zeiger zulässt.

Die schwache Referenz kann sich nicht darauf verlassen, die Referenzanzahl des Objekts zu überprüfen, auf das sie verweist. Dies liegt daran, dass dieser Speicher möglicherweise zurückgefordert wurde und jetzt von einem anderen Objekt verwendet wird. Im besten Fall stürzt die VM ab, im schlimmsten Fall ermöglicht die schwache Referenz den Zugriff auf ein Objekt, auf das sie sich ursprünglich nicht bezog. Aus diesem Grund muss der Garbage Collector die schwache Referenz informieren, deren Referent nicht mehr gültig ist.

Die Struktur und die C-API für dieses Objekt finden Sie in schwachrefobject.h . Und das Implementierungsdetail ist hier

Dünen
quelle
38

[Bearbeiten 1: Erklären Sie die Art der verknüpften Liste und wann Schwachstellen wiederverwendet werden]

Interessanterweise ist die offizielle Dokumentation zu diesem Thema nicht aufschlussreich:

Ohne eine __weakref__Variable für jede Instanz unterstützen definierende Klassen __slots__keine schwachen Verweise auf ihre Instanzen. Wenn eine schwache Referenzunterstützung benötigt wird, fügen Sie __weakref__die Zeichenfolge in der __slots__Deklaration hinzu.

Die typeObjektdokumentation zum Thema scheint nicht allzu viel zu helfen:

Wenn die __slots__Deklaration eines Typs einen Slot mit dem Namen enthält __weakref__, wird dieser Slot zum schwachen Referenzlistenkopf für Instanzen des Typs, und der Versatz des Slots wird im Typ gespeichert tp_weaklistoffset.

Schwache Referenzen bilden eine verknüpfte Liste. Der Kopf dieser Liste (der erste schwache Verweis auf ein Objekt) ist über verfügbar __weakref__. Schwache Refs werden nach Möglichkeit wiederverwendet, sodass die Liste (keine Python-Liste!) In der Regel entweder leer ist oder ein einzelnes Element enthält.

Beispiel :

Bei der ersten Verwendung weakref.ref()erstellen Sie eine neue schwache Referenzkette für das Zielobjekt. Der Kopf dieser Kette ist der neue Schwachpunkt und wird im Zielobjekt gespeichert __weakref__:

>>> import weakref
>>> class A(object): pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b)
>>> print(b is c is a.__weakref__)
True

Wie wir sehen können, bwird wiederverwendet. Wir können Python zwingen, eine neue Schwachstelle zu erstellen, indem wir beispielsweise einen Rückrufparameter hinzufügen:

>>> def callback():
>>>   pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b, callback)
>>> print(b is c is a.__weakref__)
False

Nun b is a.__weakref__und cist die zweite Referenz in der Kette. Auf die Referenzkette kann über Python-Code nicht direkt zugegriffen werden. Wir sehen nur das Kopfelement der Kette ( b), aber nicht, wie die Kette weitergeht ( b-> c).

Dies __weakref__gilt auch für den Kopf der internen verknüpften Liste aller schwachen Verweise auf das Objekt. Ich kann keine offizielle Dokumentation finden, in der diese Rolle kurz __weakref__erklärt wird, daher sollte man sich wahrscheinlich nicht auf dieses Verhalten verlassen, da es sich um ein Implementierungsdetail handelt.

dhke
quelle
Ok ... obj.__weakref__erinnert sich also an die schwache Referenz, wrdie auf objeinen Willen zeigt, der verwendet wird, um ungültig zu machen, wrwann objMüll gesammelt wird?
Michael
1
@Michael Der Garbage Collector ignoriert Schwachstellen beim Sammeln erreichbarer Objekte vollständig . Ein Schwachpunkt zählt aus Sicht des Garbage Collectors einfach nicht als Referenz. Schwachstellen werden nur beim Aufräumen berücksichtigt . Der GC durchläuft die Liste der Schwachstellen, macht sie ungültig und ruft einen Rückruf für die Schwachstellen auf.
Dhke
18

Die __weakref__Variable ist ein Attribut, mit dem das Objekt die schwachen Referenzen unterstützt und die schwachen Referenzen auf das Objekt beibehält.

In der Python-Dokumentation wurde dies wie folgt erklärt:

Wenn die einzigen verbleibenden Verweise auf einen Verweis schwache Verweise sind, kann die Garbage Collection den Verweis zerstören und seinen Speicher für etwas anderes wiederverwenden.

Daher besteht die Pflicht schwacher Referenzen darin, die Bedingungen für ein Objekt bereitzustellen, damit unabhängig von seiner Art und seinem Umfang Müll gesammelt werden kann.

Und darüber __slots__können wir uns zunächst die Dokumentation ansehen, die es sehr gut erklärt:

Standardmäßig verfügen Instanzen von Klassen über ein Wörterbuch für die Attributspeicherung. Dies verschwendet Platz für Objekte mit sehr wenigen Instanzvariablen. Der Platzverbrauch kann akut werden, wenn eine große Anzahl von Instanzen erstellt wird.

Die Standardeinstellung kann durch Definieren __slots__in einer Klassendefinition überschrieben werden . Die __slots__Deklaration verwendet eine Folge von Instanzvariablen und reserviert in jeder Instanz gerade genug Speicherplatz, um einen Wert für jede Variable zu speichern. Speicherplatz wird gespart, da __dict__nicht für jede Instanz erstellt wird.

Da __slots__Sie nun mithilfe von den erforderlichen Speicher für Ihr Attribut steuern, wird die automatische Erstellung von __dict__und __weakref__für jede Instanz tatsächlich verhindert . Welches __weakref__ist die notwendige Variable jedes Objekts, um mit schwachen Referenzen umgehen zu können.

Zusätzlich zu all diesen Angaben heißt es in der Dokumentation für den object.__slots__Unterricht:

Dieser Klassenvariablen kann eine Zeichenfolge, eine Iterable oder eine Folge von Zeichenfolgen mit Variablennamen zugewiesen werden, die von Instanzen verwendet werden. __slots__reserviert Platz für die deklarierten Variablen und verhindert die automatische Erstellung von __dict__und __weakref__für jede Instanz.

Kurz gesagt, wir können daraus schließen, dass __slots__für die manuelle Verwaltung der Speicherzuordnung und da __weakref__die Lizenz zum Akzeptieren der schwachen Referenzen für Objekte, die sich auf die Speicherung beziehen (aufgrund der Fähigkeit, Müll zu sammeln), __slots__die __weakref__as gesteuert wird sowie die Steuerung des __dict__Attributs.

Die Dokumentation hat Ihnen auch gezeigt, wie Sie ein Objekt erstellen können, um die schwachen Referenzen neben der Verwendung zu unterstützen __slots__:

Ohne eine __weakref__Variable für jede Instanz unterstützen definierende Klassen __slots__keine schwachen Verweise auf ihre Instanzen. Wenn eine schwache Referenzunterstützung benötigt wird, fügen Sie '__weakref__'die Zeichenfolge in der __slots__Deklaration hinzu.

Hier ist ein Beispiel in Python 3.X:

>>> class Test:
...     __slots__ = ['a', 'b']
... 
>>> 
>>> import weakref
>>> 
>>> t = Test()
>>> 
>>> r = weakref.ref(t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create weak reference to 'Test' object
>>> 
>>> class Test:
...     __slots__ = ['a', 'b', '__weakref__']
... 
>>> t = Test()
>>> r = weakref.ref(t)
>>> 
>>> t.__weakref__
<weakref at 0x7f735bc55d68; to 'Test' at 0x7f735bc51fc8>

In Python 2.7 wird jedoch, obwohl die Dokumentation den oben genannten Dokumenten ähnelt, das Erstellen einer schwachen Referenz aus Instanzen, die die __weakref__Variable in ihren __slots__Namen nicht enthalten, nicht ausgelöst TypeError:

>>> class Test:
...    __slots__ = ['a', 'b']
... 
>>> t = Test()
>>> 
>>> r = weakref.ref(t)
>>> 
>>> r
<weakref at 0x7fe49f4185d0; to 'instance' at 0x7fe4a3e75f80>
Kasravnd
quelle
2
Ich sehe in den Anführungszeichen nichts darüber, was __weakref__tatsächlich funktioniert (es enthält den schwachen Referenz-Singleton). Möchtest du das hinzufügen?
Dhke
1
@dhke: Gleich hier ... Das Konzept der schwachen Referenzen ist klar, ich frage mich wirklich über die genaue Bedeutung und Funktion von __weakref__. Ich vermute, dass es möglicherweise so trivial ist, dass sie es einfach weggelassen haben ...
Michael
In Anbetracht Ihrer Bearbeitung und des allerletzten Satzes im Beitrag: __weakref__ist nur eine Art Flagge, die sagt: "Ja, ich kann schwach referenziert werden." (?)
Michael
@ Michael Ja, ich denke, ein Beispiel wird es besser beschreiben.
Kasravnd