2011-08-07 6 views
5

Duplicar posible:
Why doesn't the weakref work on this bound method?¿Puedo usar métodos de referencia débiles?

Un poco de contexto:

yo estaba tratando de implementar un oyente (o Observador, la misma cosa) patrón: Un EventManager mantiene una lista de todos los manejadores de Oyentes interesados ​​en un Evento. Por ejemplo, un objeto Listener tendría un método onEndOfTheWorldEvent que sería llamado por el EventManager cada vez que se publicara una instancia de la clase de evento EndOfTheWorldEvent. Fácil.

Excepto que quería hacer una referencia débil a los controladores porque no quiero que el EventManager mantenga mis manejadores (métodos encuadernados) vivos cuando el Listener ya no se necesite.

Así que pensé "Vamos a lanzar todos los controladores en un WeakSet". No pude hacer que funcione.

Introduzco aquí el código (o lo que queda de él cuando lo reduzco al mínimo, aquí solo hay un tipo de evento y solo un tipo de controlador).

#! /usr/bin/python 
""" 

""" 
import sys 
import weakref 

class Listener(object): 
    def handler(self, event): 
     print event 

class EventManager(object): 
    def __init__(self): 
     self.handlers = weakref.WeakSet() 
    def register(self, listener): 
     print "Registering..." 
     self.handlers.add(listener.handler) 
     CountRefs(listener.handler) 
     print "Number of handlers registered:", len(self.handlers) 
     print "Registered." 

def CountRefs(what): 
    print "Hard count:", sys.getrefcount(what) 
    print "Weak count:", weakref.getweakrefcount(what) 

listener = Listener() 
em = EventManager() 
CountRefs(listener.handler) 
em.register(listener) 
CountRefs(listener.handler) 

resultado:

Hard count: 3 
Weak count: 0 
Registering... 
Hard count: 3 
Weak count: 0 
Number of handlers registered: 0 
Registered. 
Hard count: 3 
Weak count: 0 

Sólo se ve como nunca hay ninguna referencia débil, y el conjunto queda vacía.

para que sea aún más sencillo:

>>> class C(object): 
>>>  def blah(self): 
>>>   print "blah" 
>>> 
>>> c = C() 
>>> w = weakref.ref(c.blah) 
>>> print w 
<weakref at 0x11e59f0; dead> 

no puedo crear weakrefs a métodos en absoluto? Si no, ¿por qué no?

Así que supongo que una solución sería reemplazar el WeakSet con un WeakKeyDictionary: la clave es el oyente en sí mismo, y valora el controlador. De hecho, puedo debilitar a mis oyentes. Pero hace que la estructura de datos sea un poco más complicada, y cuando llega el momento de transmitir los eventos a todo el mundo, hay un nivel más en esa estructura para atravesar.

¿Qué opinas?

+0

Esta pregunta se hace no realmente duplicado, porque se enfoca en una solución y no solo en "por qué no". Dejen solo la buena respuesta aceptada aquí. En el mejor de los casos, puede marcar la otra pregunta como duplicada en el sentido de: "ésta implica la respuesta para el otro". – kxr

Respuesta

8

Digamos que quiere weakrefs en un método "meth".

Puede obtener weakrefs en él como esto

weak_obj = weakref.ref(meth.im_self) 
weak_func = weakref.ref(meth.im_func) 

lo tanto, puede DEREF de esa manera

obj = weak_obj() 
func = weak_func() 

y obtener "meth" espalda con

meth = getattr(obj, func.__name__) 
+0

Guau, buen truco. Nunca tuve que mirar estos im_self y co antes. – Niriel

+0

En realidad, lo necesitaba, porque el método enlazado hace referencia a la instancia y, por lo tanto, la instancia no desaparece del WeakKeyDictionary. Gracias ! – Niriel

+3

Parece que desde Python 2.6 avanzando, el acceso preferido para estos es 'bound_method .__ self__' para el objeto, y' bound_method .__ func__' para la función. –

2

listener.handler le da una nueva referencia enlazada a la función cada vez. Por lo tanto, se recolecta la basura casi de inmediato.

+0

De hecho, la comparación con "es" siempre devuelve False. Por otra parte, pensé que podría haber sido una convención extraña. – Niriel

Cuestiones relacionadas