web-dev-qa-db-fra.com

Quel est exactement __weakref__ en Python?

É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?

55
Michael

__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

43
Dunes

[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 le tp_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.

26
dhke

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>
16
Kasrâmvd