Quand utiliser des références faibles en Python?

quelqu'un Peut-il expliquer l'utilisation de références faibles?

documentation ne l'explique pas précisément, il dit simplement que le GC peut détruire l'objet lié à via une référence faible à tout moment. Alors à quoi sert d'avoir un objet qui peut disparaître à tout moment? Et si je dois l'utiliser juste après sa disparition?

Pouvez-vous expliquer avec quelques bons exemples?

Merci

32
demandé sur eLRuLL 2010-03-13 01:25:20

3 réponses

l'utilisation typique pour les références faibles est si A a une référence à B et B a une référence à A. Sans un collecteur de déchets de détection de cycle approprié, ces deux objets n'obtiendraient jamais GC'd Même s'il n'y a aucune référence à l'un ou l'autre de "l'extérieur". Toutefois, si l'une des références est "faible", les objets sont correctement GC.

Cependant, Python avoir un collecteur d'ordures à détection de cycle (depuis 2.0!), donc ça ne compte pas :)

une Autre utilisation pour les références faibles est pour les caches. Il est mentionné dans le weakref documentation:

une utilisation principale pour les références faibles est d'implémenter des caches ou des mappages contenant de grands objets, où il est souhaitable qu'un grand objet ne soit pas maintenu en vie uniquement parce qu'il apparaît dans une cache ou une mapping.

si le GC décide de détruire un de ces objets, et que vous en avez besoin, vous pouvez simplement recalculer / recalculer les données.

24
répondu Nicolás 2010-03-12 22:36:56

les événements sont un scénario courant pour les références faibles.


Problème

considérez une paire d'objets: émetteur et Récepteur. Le récepteur a une durée de vie plus courte que l'émetteur.

Vous pouvez essayer une mise en œuvre comme ceci:

class Emitter(object):

    def __init__(self):
        self.listeners = set()

    def emit(self):
        for listener in self.listeners:
            # Notify
            listener('hello')


class Receiver(object):

    def __init__(self, emitter):

        emitter.listeners.add(self.callback)

    def callback(self, msg):
        print 'Message received:', msg


e = Emitter()
l = Receiver(e)
e.emit() # Message received: hello

Toutefois, dans ce cas, l'Émetteur conserve une référence à une méthode liée callback qui maintient une référence vers le Récepteur. Donc L'émetteur garde le récepteur vivant:

# ...continued...

del l
e.emit() # Message received: hello

C'est parfois gênant. Imaginez que Emitter fait partie d'un modèle de données qui indique quand les données changent et Receiver a été créé par une fenêtre de dialogue qui écoute les changements pour mettre à jour certains contrôles de L'interface.

au cours de la durée de vie de l'application, plusieurs dialogues peuvent être générés et nous ne voulons pas que leurs récepteurs soient encore enregistrés à l'intérieur de l'émetteur longtemps après la fermeture de la fenêtre. Que serait un mémoire fuite.

supprimer les callbacks manuellement est une option (tout aussi gênante), utiliser des références faibles en est une autre.


Solution

There is a nice class WeakSet cela ressemble à un ensemble normal mais stocke ses membres en utilisant des références faibles et ne les stocke plus quand ils sont libérés.

Excellent! Nous allons l'utiliser:

def __init__(self):
    self.listeners = weakref.WeakSet()

et exécutez de nouveau:

e = Emitter()
l = Receiver(e)
e.emit()
del l
e.emit()

Oh, il ne se passe rien du tout! C'est parce que la méthode liée (un récepteur spécifique est callback) est orphelin maintenant - ni l'Émetteur ni le Récepteur ont une forte référence à elle. Par conséquent, ce sont des ordures collectées immédiatement.

laissons le récepteur (pas L'émetteur cette fois) garder une référence forte à ce rappel:

class Receiver(object):

    def __init__(self, emitter):

        # Create the bound method object
        cb = self.callback

        # Register it
        emitter.listeners.add(cb)
        # But also create an own strong reference to keep it alive
        self._callbacks = set([cb])

nous pouvons maintenant observer le comportement attendu: l'émetteur ne garde le rappel que pendant la durée de vie du récepteur.

e = Emitter()
l = Receiver(e)
assert len(e.listeners) == 1

del l
import gc; gc.collect()
assert len(e.listeners) == 0

en Vertu de la capot

Notez que j'ai dû mettre un gc.collect() ici pour assurez-vous que le récepteur est vraiment nettoyé immédiatement. Il est nécessaire ici parce qu'il y a maintenant un cycle de références fortes: la méthode liée se réfère au récepteur et vice versa.

ce n'est pas très mauvais; cela signifie seulement que le nettoyage du récepteur sera reporté jusqu'à la prochaine course de ramasseur d'ordures. Les références cycliques ne peuvent pas être nettoyées par le simple mécanisme de comptage de référence.

si vous voulez vraiment, vous pouvez supprimer le cycle de référence fort en remplaçant la méthode liée par un objet de fonction personnalisé qui garderait son self comme une référence faible.

def __init__(self, emitter):

    # Create the bound method object
    weakself = weakref.ref(self)
    def cb(msg):
        self = weakself()
        self.callback(msg)

    # Register it
    emitter.listeners.add(cb)
    # But also create an own strong reference to keep it alive
    self._callbacks = set([cb])

mettons cette logique dans une fonction helper:

def weak_bind(instancemethod):

    weakref_self = weakref.ref(instancemethod.im_self)
    func = instancemethod.im_func

    def callback(*args, **kwargs):
        self = weakref_self()
        bound = func.__get__(self)
        return bound(*args, **kwargs)

    return callback

class Receiver(object):

    def __init__(self, emitter):

        cb = weak_bind(self.callback)

        # Register it
        emitter.listeners.add(cb)
        # But also create an own strong reference to keep it alive
        self._callbacks = set([cb])

maintenant il n'y a pas de cycle de références fortes, donc quand Receiver est libéré, la fonction de rappel sera également libéré (et retiré de l'Émetteur WeakSet) immédiatement, sans avoir besoin d'un cycle complet de GC.

29
répondu Kos 2014-03-21 12:06:04
 - Weak references is an important concept in python, which is missing
   in languages likes Java(java 1.5).
 - In Observer design pattern, generally Observable Object must maintain
   weak references to the Observer object.

   eg. A emits an event done() and B registers with A that, it want to
   listen to event done(). Thus, whenever done() is emitted, B is
   notified. But If B isn't required in application, then A must not
   become an hinderance in the garbage collection in A(since A hold the
   reference to B). Thus, if A has hold weak reference to B, and when
   all the references to A are away, then B will be garbage collected.
 - It's also very useful in implementing caches.
1
répondu Mangu Singh Rajpurohit 2016-10-30 16:58:34