Étonnamment, il n'y a pas de documentation explicite pour __weakref__
. Les références faibles sont expliquées ici . __weakref__
est également brièvement mentionné dans la documentation de __slots__
. Mais je n'ai rien trouvé sur __weakref__
lui-même.
Quel est exactement __weakref__
? - S'agit-il simplement d'un membre agissant comme un drapeau: s'il est présent, l'objet peut être faiblement référencé? - Ou s'agit-il d'une fonction/variable qui peut être remplacée/affectée pour obtenir le comportement souhaité? Comment?
__weakref__
est juste un objet opaque qui référence toutes les références faibles à l'objet courant. En fait, c'est une instance de weakref
(ou parfois weakproxy
) qui est à la fois une référence faible à l'objet et une partie d'une liste doublement liée à toutes les références faibles de cet objet.
C'est juste un détail d'implémentation qui permet au garbage collector d'informer les références faibles que son référent a été collecté, et de ne plus autoriser l'accès à son pointeur sous-jacent.
La référence faible ne peut pas s'appuyer sur la vérification du nombre de références de l'objet auquel elle se réfère. En effet, cette mémoire a peut-être été récupérée et est maintenant utilisée par un autre objet. Dans le meilleur des cas, le VM plantera, dans le pire des cas, la référence faible permettra d'accéder à un objet auquel il ne faisait pas référence à l'origine. C'est pourquoi le garbage collector doit informer la référence faible que son référent est n'est plus valide.
Voir faiblesrefobject.h pour la structure et C-API pour cet objet. Et le détail de l'implémentation est ici
[Edit 1: Expliquez la nature de la liste chaînée et quand les références faibles sont réutilisées]
Chose intéressante, la documentation officielle est quelque peu non éclairante sur ce sujet:
Sans variable
__weakref__
Pour chaque instance, les classes définissant__slots__
Ne prennent pas en charge les références faibles à ses instances. Si un support de référence faible est nécessaire, ajoutez__weakref__
À la séquence de chaînes dans la déclaration__slots__
.
La documentation de l'objet type
sur le sujet ne semble pas trop aider les choses:
Lorsque la déclaration
__slots__
D'un type contient un emplacement nommé__weakref__
, Cet emplacement devient la tête de liste de référence faible pour les instances du type et le décalage de l'emplacement est stocké dans letp_weaklistoffset
Du type .
Les références faibles forment une liste chaînée. Le titre de cette liste (la première référence faible à un objet) est disponible via __weakref__
. Les références faibles sont réutilisées chaque fois que possible, donc la liste (pas une liste Python!) Est généralement vide ou contient un seul élément.
Exemple :
Lorsque vous utilisez pour la première fois weakref.ref()
, vous créez une nouvelle chaîne de référence faible pour l'objet cible. La tête de cette chaîne est la nouvelle faiblesse et est stockée dans le __weakref__
De l'objet cible:
>>> import weakref
>>> class A(object): pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b)
>>> print(b is c is a.__weakref__)
True
Comme nous pouvons le voir, b
est réutilisé. Nous pouvons forcer python pour créer une nouvelle référence de faiblesse, en ajoutant par exemple un paramètre de rappel:
>>> def callback():
>>> pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b, callback)
>>> print(b is c is a.__weakref__)
False
Maintenant b is a.__weakref__
Et c
est la deuxième référence de la chaîne. La chaîne de référence n'est pas directement accessible à partir de Python. Nous ne voyons que l'élément head de la chaîne (b
), mais pas comment la chaîne continue (b
-> c
).
Ainsi, __weakref__
Est la tête de la liste interne liée de toutes les références faibles à l'objet. Je ne trouve aucun document officiel où ce rôle de __weakref__
Est expliqué de manière concise, donc il ne faut probablement pas se fier à ce comportement, car c'est un détail d'implémentation.
Le __weakref__
variable est un attribut qui fait que l'objet supporte les références faibles et préserve les références faibles à l'objet.
La documentation python l'a expliqué comme suit:
lorsque les seules références restantes à un référent sont des références faibles, la récupération de place est libre de détruire le référent et de réutiliser sa mémoire pour autre chose.
Par conséquent, le devoir des références faibles est de fournir les conditions pour un objet afin de pouvoir être récupéré indépendamment de son type et de sa portée.
Et sur le __slots__
, on peut d'abord regarder la documentation, ce qui l'explique très bien:
Par défaut, les instances de classes ont un dictionnaire pour le stockage des attributs. Cela gaspille de l'espace pour les objets ayant très peu de variables d'instance. La consommation d'espace peut devenir aiguë lors de la création d'un grand nombre d'instances.
La valeur par défaut peut être remplacée en définissant
__slots__
dans une définition de classe. Le__slots__
la déclaration prend une séquence de variables d'instance et réserve juste assez d'espace dans chaque instance pour contenir une valeur pour chaque variable. L'espace est économisé car__dict__
n'est pas créé pour chaque instance.
Maintenant, puisque en utilisant __slots__
vous contrôlerez le stockage demandé pour votre attribut, cela empêche en fait la création automatique de __dict__
et __weakref__
pour chaque instance. Lequel à __weakref__
est la variable nécessaire de chaque objet pour pouvoir traiter les références faibles.
En plus de tout cela, la documentation de object.__slots__
la classe dit:
Cette variable de classe peut se voir affecter une chaîne, un itérable ou une séquence de chaînes avec des noms de variables utilisés par les instances.
__slots__
réserve de l'espace aux variables déclarées et empêche la création automatique de__dict__
et__weakref__
pour chaque instance.
Donc, en résumé, nous pouvons conclure que __slots__
sert à gérer l'allocation de stockage manuellement et depuis __weakref__
est la licence d'acceptation des références faibles pour les objets qui est liée au stockage (en raison de la possibilité d'être récupéré), donc __slots__
contrôlera le __weakref__
ainsi que le contrôle de __dict__
attribut.
De plus, la documentation vous a montré comment créer un objet pour prendre en charge les références faibles parallèlement à l'utilisation de __slots__
:
Sans un
__weakref__
variable pour chaque instance, classes définissant__slots__
ne prend pas en charge les références faibles à ses instances. Si un support de référence faible est nécessaire, ajoutez'__weakref__'
à la séquence de chaînes dans le__slots__
déclaration.
Voici un exemple dans 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>
Mais dans python 2.7 là-bas, bien que la documentation soit comme les documents susmentionnés, créant une référence faible à partir d'instances qui ne fournissent pas le __weakref__
variable dans leur __slots__
les noms ne soulèvent pas un TypeError
:
>>> class Test:
... __slots__ = ['a', 'b']
...
>>> t = Test()
>>>
>>> r = weakref.ref(t)
>>>
>>> r
<weakref at 0x7fe49f4185d0; to 'instance' at 0x7fe4a3e75f80>