【问题标题】:weakref list in pythonpython中的弱引用列表
【发布时间】:2009-03-24 15:39:59
【问题描述】:

我需要一个弱引用列表,以便在项目死亡时删除它们。目前我这样做的唯一方法是继续刷新列表(手动删除死引用)。

我知道有 WeakKeyDictionary 和 WeakValueDictionary,但我真的很喜欢 WeakList,有没有办法做到这一点?

这是一个例子:

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()

【问题讨论】:

  • 您能否定义“die”、“reference”、“flush”、“手动删除”?没有这些,这个问题真的没有多大意义。
  • die = 弱引用变得无效(“死”)。参考 = 弱参考 (docs.python.org/library/weakref.html)。 flush = 手动删除 = 遍历列表中的所有引用,删除那些无效的。
  • 如果您改用普通列表,则无需刷新任何内容...您的示例似乎很做作。你的实际观点是什么?
  • 是的,这是人为的。我试图创建一个简单的例子来说明,实际的解决方案比这复杂得多。我所追求的只是一个包含弱引用的列表,这些引用将在解除分配时自动删除。如果不存在,我将创建自己的来执行此操作。
  • 普通对象在没有引用时自动删除。没有什么“额外”需要做的。请提供一些您正在尝试做的不适合普通垃圾收集的感觉。

标签: python weak-references


【解决方案1】:

您可以自己实现它,与您的做法类似,但在尝试访问项目之前使用一个调用 flush() 的列表子类。

显然,您不想在每次访问时都这样做,但您可以通过在弱引用上设置回调来优化此操作,以便在某些东西死亡时将列表标记为脏。然后,您只需要在自上次访问后发生某些事情时刷新列表。

这是一个使用此方法实现的列表类。 (请注意,它没有经过太多测试,并且某些方法的实现效率不是很高(例如,那些只是转换为真实列表并在其上调用方法的方法),但这应该是一个合理的起点:

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

【讨论】:

  • 当列表大小在迭代期间发生变化时,它的行为如何?我说的是__iter__remove 中的循环。
【解决方案2】:

因为我需要像你这样的弱引用列表,所以我制作了一个并在 pypi 上发布。

现在你可以这样做了:

pip install weakreflist

然后:

from weakreflist import WeakList

【讨论】:

    【解决方案3】:

    您可以使用来自同一个weakref 模块的WeakSet(顺便说一下,它实际上在别处定义,但它是在那里导入的)。

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

    【讨论】:

    • WeakSet 只能存储可散列的对象。
    • 它也有不保留其条目顺序的缺点。
    • @PeterGraham 和 TokenMacGuy:OP 没有说明他对订购的要求。因此,如果他只是想要一个 [无序] 袋弱引用的东西,并且如果这些东西是可散列的,那么 WeakSet 的想法看起来是可行的。如果这些东西是他自己设计的对象,也许他可以在自己的对象中满足可哈希的要求。
    【解决方案4】:

    你为什么不能这样做:

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

    然后对__iadd__extend 等执行类似操作。 对我有用。

    【讨论】:

    • 嗯...这行不通。我签入了 Python 2.6 和 3.1,但都不支持第二个参数:TypeError: append() takes exactly one argument (2 given)
    • 那将是 super(list, self).append 等
    • @Mu Mind:检查括号!
    【解决方案5】:

    您打算如何使用B?我对我建立的弱引用列表所做的唯一事情就是迭代它,所以它的实现很简单:

    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]
    

    【讨论】:

      【解决方案6】:

      使用传递给弱引用的第二个参数的回调函数。

      这段代码应该起作用:

      import weakref
      
      class WeakRefList(list):
      
          def add_reference(self, obj):
              self._references.append(weakref.proxy(obj, self.remove))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-27
        • 2013-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多