2009-03-24 6 views
16

Necesito una lista de referencias débiles que eliminan elementos cuando mueren. Actualmente, la única forma que tengo de hacer esto es mantener el lavado de la lista (eliminando las referencias muertas manualmente).lista de weakref en python

Soy consciente de que hay una WeakKeyDictionary y una WeakValueDictionary, pero estoy realmente después de un WeakList, hay una manera de hacer esto?

He aquí un ejemplo:

import weakref 

class A(object): 
    def __init__(self): 
     pass 

class B(object): 
    def __init__(self): 
     self._references = [] 

    def addReference(self, obj): 
     self._references.append(weakref.ref(obj)) 

    def flush(self): 
     toRemove = [] 

     for ref in self._references: 
      if ref() is None: 
       toRemove.append(ref) 

     for item in toRemove: 
      self._references.remove(item) 

b = B() 

a1 = A() 
b.addReference(a1) 
a2 = A() 
b.addReference(a2) 

del a1 
b.flush() 
del a2 
b.flush() 
+0

¿podría por favor defina "morir", "referencia", "flush", "eliminación manual"? esta pregunta realmente no tiene mucho sentido sin esos. – hop

+0

dado = la referencia débil se vuelve inválida ("muerta"). referencia = una referencia débil (http://docs.python.org/library/weakref.html). flush = eliminación manual = iterar sobre todas las referencias en la lista eliminando aquellas que no son válidas. – Dan

+0

todavía no es muy claro, a menos que ya sepa qué hacer – hop

Respuesta

5

Se puede aplicar por sí mismo, de manera similar a cómo se ha hecho, pero con una subclase lista que llama a flush() antes de intentar acceder a un elemento.

Obviamente, no desea hacer esto en cada acceso, pero puede optimizar esto configurando una devolución de llamada en la referencia débil para marcar la lista como sucia cuando algo muere. Entonces solo necesita vaciar la lista cuando algo ha muerto desde el último acceso.

Aquí hay una clase de lista implementada con este método. (Tenga en cuenta que no se prueba mucho, y algunos métodos no se implementan de manera muy eficiente (por ejemplo, aquellos que simplemente se convierten a una lista real y llaman al método), pero debería ser un punto de partida razonable:

import weakref 

class WeakList(list): 
    def __init__(self, seq=()): 
     list.__init__(self) 
     self._refs = [] 
     self._dirty=False 
     for x in seq: self.append(x) 

    def _mark_dirty(self, wref): 
     self._dirty = True 

    def flush(self): 
     self._refs = [x for x in self._refs if x() is not None] 
     self._dirty=False 

    def __getitem__(self, idx): 
     if self._dirty: self.flush() 
     return self._refs[idx]() 

    def __iter__(self): 
     for ref in self._refs: 
      obj = ref() 
      if obj is not None: yield obj 

    def __repr__(self): 
     return "WeakList(%r)" % list(self) 

    def __len__(self): 
     if self._dirty: self.flush() 
     return len(self._refs) 

    def __setitem__(self, idx, obj): 
     if isinstance(idx, slice): 
      self._refs[idx] = [weakref.ref(obj, self._mark_dirty) for x in obj] 
     else: 
      self._refs[idx] = weakref.ref(obj, self._mark_dirty) 

    def __delitem__(self, idx): 
     del self._refs[idx] 

    def append(self, obj): 
     self._refs.append(weakref.ref(obj, self._mark_dirty)) 

    def count(self, obj): 
     return list(self).count(obj) 

    def extend(self, items): 
     for x in items: self.append(x) 

    def index(self, obj): 
     return list(self).index(obj) 

    def insert(self, idx, obj): 
     self._refs.insert(idx, weakref.ref(obj, self._mark_dirty)) 

    def pop(self, idx): 
     if self._dirty: self.flush() 
     obj=self._refs[idx]() 
     del self._refs[idx] 
     return obj 

    def remove(self, obj): 
     if self._dirty: self.flush() # Ensure all valid. 
     for i, x in enumerate(self): 
      if x == obj: 
       del self[i] 

    def reverse(self): 
     self._refs.reverse() 

    def sort(self, cmp=None, key=None, reverse=False): 
     if self._dirty: self.flush() 
     if key is not None: 
      key = lambda x,key=key: key(x()) 
     else: 
      key = apply 
     self._refs.sort(cmp=cmp, key=key, reverse=reverse) 

    def __add__(self, other): 
     l = WeakList(self) 
     l.extend(other) 
     return l 

    def __iadd__(self, other): 
     self.extend(other) 
     return self 

    def __contains__(self, obj): 
     return obj in list(self) 

    def __mul__(self, n): 
     return WeakList(list(self)*n) 

    def __imul__(self, n): 
     self._refs *= n 
     return self 

[Editar] Agregar una aplicación lista más completa

+0

¿Cómo se comporta eso cuando el tamaño de la lista cambia durante una iteración? Estoy hablando del ciclo en '__iter__' y eso en' remove'. – Niriel

1

por qué no puedes simplemente hacerlo de esta manera:..

import weakref 

class WeakList(list): 
    def append(self, item): 
     list.append(self, weakref.ref(item, self.remove)) 

y luego hacer similar para __iadd__, etc. extend funciona para mí

+1

Umm ... eso no funciona. Comprobé en Python 2.6 y 3.1 y ninguno admite un segundo argumento: 'TypeError: append() toma exactamente un argumento (2)' –

+0

que sería super (lista, self) .append etc. –

+0

@Mu Mente: ¡marque paréntesis! – user508402

5

Puede usar WeakSet desde el mismo módulo weakref (en realidad está definido en otra parte por cierto, pero se importa allí).

>>> from weakref import WeakSet 
>>> s = WeakSet() 
>>> class Obj(object): pass # can't weakref simple objects 
>>> a = Obj() 
>>> s.add(a) 
>>> print len(s) 
1 
>>> del a 
>>> print len(s) 
0 
+3

Un WeakSet solo puede almacenar objetos lavables. –

+4

también tiene el inconveniente de no preservar el orden de sus entradas. – SingleNegationElimination

+2

@PeterGraham y TokenMacGuy: el OP no estableció sus requisitos en cuanto a los pedidos. Entonces, si solo se le deseara una bolsa [no ordenada] de cosas débilmente refinadas, y si esas cosas son lavables, la idea de WeakSet parece viable. Si las cosas son objetos de su propio diseño, tal vez pueda cumplir con el requisito de tener que cargar en su propio objeto. –

0

¿Cómo piensa utilizar B? La única cosa que haga con la lista weakref he construido es iterar sobre ella, por lo que su aplicación es simple:

import weakref 

class WeakRefList(object): 
    "weakref psuedo list" 
    def __init__(yo): 
     yo._items = list() 
    def __iter__(yo): 
     yo._items = [s for s in yo._items if s() is not None] 
     return (s() for s in yo._items if s() is not None) 
    def __len__(yo): 
     yo._items = [s for s in yo._items if s() is not None] 
     return len(yo._items) 
    def append(yo, new_item): 
     yo._items.append(weakref.ref(new_item)) 
     yo._items = [s for s in yo._items if s() is not None] 
2

utilizar una función de devolución de llamada pasa al segundo argumento de un weakref.

Este código debe funcionar:

import weakref 

class weakRefList(list): 

    def addReference(self, obj): 
     self._references.append(weakref.proxy(obj, self.remove)) 
5

ya que necesitaba una lista weakref como usted, he hecho una y publicarla en PyPI.

ahora se puede hacer:

pip install weakreflist 

a continuación:

from weakreflist import WeakList