2009-03-24 29 views
16

Tôi cần một danh sách các tài liệu tham khảo yếu để xóa các mục khi chúng chết. Hiện tại, cách duy nhất tôi có để làm điều này là tiếp tục xóa danh sách (loại bỏ các tham chiếu chết theo cách thủ công).danh sách weakref trong python

Tôi biết rằng có một WeakKeyDictionary và WeakValueDictionary, nhưng tôi thực sự sau một WeakList, có cách nào để làm điều này không?

Dưới đây là một ví dụ:

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

bạn có thể xin định nghĩa "chết", "tham khảo", "tuôn ra", "loại bỏ bằng tay"? câu hỏi này thực sự không có ý nghĩa gì nếu không có những câu hỏi đó. – hop

+0

die = tham chiếu yếu trở thành không hợp lệ ("đã chết"). tham chiếu = tham chiếu yếu (http://docs.python.org/library/weakref.html). flush = remove manually = lặp qua tất cả các tham chiếu trong danh sách loại bỏ những tham chiếu không hợp lệ. – Dan

+0

vẫn không rõ ràng, trừ khi bạn đã biết phải làm gì – hop

Trả lời

5

Bạn có thể thực hiện điều đó cho mình, tương tự như cách bạn đã làm, nhưng với một lớp con danh sách mà các cuộc gọi flush() trước khi cố gắng truy cập vào một mục. Rõ ràng là bạn không muốn làm điều này trên mọi truy cập, nhưng bạn có thể tối ưu hóa điều này bằng cách thiết lập một cuộc gọi lại trên tham chiếu yếu để đánh dấu danh sách bẩn khi một cái gì đó chết. Sau đó, bạn chỉ cần tuôn ra danh sách khi một cái gì đó đã chết kể từ lần truy cập cuối cùng.

Dưới đây là danh sách lớp được triển khai bằng phương pháp này. (Lưu ý rằng nó không được thử nghiệm nhiều, và một số phương pháp không được thực hiện rất hiệu quả (ví dụ: những phương thức chỉ chuyển đổi thành danh sách thực và gọi phương thức đó), nhưng nó phải là điểm khởi đầu hợp lý:

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 

[chỉnh sửa] thêm một thực hiện danh sách đầy đủ hơn

+0

Làm thế nào để hoạt động khi kích thước danh sách thay đổi trong một lần lặp? Tôi đang nói về vòng lặp trong '__iter__' và trong' remove'. – Niriel

1

Tại sao có thể không chỉ làm điều đó như thế này:..

import weakref 

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

Và sau đó làm tương tự cho __iadd__, extend, vv làm việc cho tôi

+1

Umm ... không hoạt động. Tôi đã kiểm tra trong Python 2.6 và 3.1 và không hỗ trợ đối số thứ 2: 'TypeError: append() lấy chính xác một đối số (2 đã cho)' –

+0

sẽ là siêu (danh sách, tự) .append etc –

+0

@Mu Mind: kiểm tra dấu ngoặc đơn! – user508402

5

Bạn có thể sử dụng WeakSet từ mô-đun weakref rất giống nhau (nó thực sự được định nghĩa ở nơi khác bằng cách này, nhưng nó được nhập ở đó).

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

Một WeakSet chỉ có thể lưu trữ các đối tượng có thể băm. –

+4

nó cũng có thiếu sót không bảo tồn thứ tự của các mục nhập của nó. – SingleNegationElimination

+2

@PeterGraham và TokenMacGuy: OP không nêu rõ các yêu cầu của ông về đặt hàng. Vì vậy, nếu anh ta chỉ muốn một túi [không có thứ tự] của những thứ yếu kém, và nếu những thứ đó có thể băm, ý tưởng WeakSet có thể hoàn toàn khả thi. Nếu vật thể là vật thể của thiết kế riêng của anh ta, có lẽ anh ta có thể đáp ứng yêu cầu băm trong vật thể của mình. –

0

Bạn định sử dụng B như thế nào? Điều duy nhất mà tôi từng làm với các danh sách weakref tôi đã xây dựng là lặp qua nó, vì vậy việc thực hiện rất đơn giản:

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

Sử dụng một hàm callback thông qua với số thứ hai của một weakref.

Mã này nên hoạt động:

import weakref 

class weakRefList(list): 

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

Như tôi cần một danh sách weakref như bạn, tôi đã thực hiện một và xuất bản nó trên pypi.

bây giờ bạn có thể làm:

pip install weakreflist 

thì:

from weakreflist import WeakList 
Các vấn đề liên quan