2011-10-19 22 views
10

Pythonで索引付け可能な弱い順序付きセットを簡単に作成できるかどうかは疑問でした。私は自分で建てようとしました。ここで私が思いついたのは次のとおりです。Pythonで索引付け可能な弱い順序付きセット

""" 
An indexable, ordered set of objects, which are held by weak reference. 
""" 
from nose.tools import * 
import blist 
import weakref 


class WeakOrderedSet(blist.weaksortedset): 
    """ 
    A blist.weaksortedset whose key is the insertion order. 
    """ 
    def __init__(self, iterable=()): 
     self.insertion_order = weakref.WeakKeyDictionary() # value_type to int 
     self.last_key = 0 
     super().__init__(key=self.insertion_order.__getitem__) 
     for item in iterable: 
      self.add(item) 

    def __delitem__(self, index): 
     values = super().__getitem__(index) 
     super().__delitem__(index) 
     if not isinstance(index, slice): 
      # values is just one element 
      values = [values] 
     for value in values: 
      if value not in self: 
       del self.insertion_order[value] 

    def add(self, value): 
     # Choose a key so that value is on the end. 
     if value not in self.insertion_order: 
      key = self.last_key 
      self.last_key += 1 
      self.insertion_order[value] = key 
     super().add(value) 

    def discard(self, value): 
     super().discard(value) 
     if value not in self: 
      del self.insertion_order[value] 

    def remove(self, value): 
     super().remove(value) 
     if value not in self: 
      del self.insertion_order[value] 

    def pop(self, *args, **kwargs): 
     value = super().pop(*args, **kwargs) 
     if value not in self: 
      del self.insertion_order[value] 

    def clear(self): 
     super().clear() 
     self.insertion_order.clear() 

    def update(self, *args): 
     for arg in args: 
      for item in arg: 
       self.add(item) 


if __name__ == '__main__': 
    class Dummy: 
     def __init__(self, value): 
      self.value = value 

    x = [Dummy(i) for i in range(10)] 
    w = WeakOrderedSet(reversed(x)) 
    del w[2:8] 
    assert_equals([9,8,1,0], [i.value for i in w]) 
    del w[0] 
    assert_equals([8,1,0], [i.value for i in w]) 
    del x 
    assert_equals([], [i.value for i in w]) 

これは簡単な方法ですか?

答えて

24

標準ライブラリの既存のコンポーネントを利用する最も簡単な方法です。

OrderedDictとMutableSet ABCを使用すると、OrderedSetを簡単に書き込むことができます。

同様に、既存のweakref.WeakSetを再利用することができますし、OrderedSetとその基礎となるセットを()に置き換えます。いつものように、

import collections, weakref 

class OrderedSet(collections.MutableSet): 
    def __init__(self, values=()): 
     self._od = collections.OrderedDict().fromkeys(values) 
    def __len__(self): 
     return len(self._od) 
    def __iter__(self): 
     return iter(self._od) 
    def __contains__(self, value): 
     return value in self._od 
    def add(self, value): 
     self._od[value] = None 
    def discard(self, value): 
     self._od.pop(value, None) 

class OrderedWeakrefSet(weakref.WeakSet): 
    def __init__(self, values=()): 
     super(OrderedWeakrefSet, self).__init__() 
     self.data = OrderedSet() 
     for elem in values: 
      self.add(elem) 
+1

非常に良い! 'weakref.WeakSet'の' data'メンバーはどこに文書化されていますか? –

+4

WeakSetのドキュメントは不完全です(ほとんど存在しません)。 –

+1

Pypyは同じ(または非常に似たような) 'WeakSet'実装を使用していますので、これも同様に動作します(weakrefsを削除するには' gc.collect() 'が必要です)。 – simonzack

1

レイモンドは素晴らしいと簡潔な答えを持っていますが、私は実際にはしばらくここに来ました索引付け可能な部分に興味を持ち、weakref部分以上のもの。私は最終的に自分の答えを作りました。それはthe IndexedSet type in the boltons utility libraryになりました。基本的には、listset APIのすべての部分が組み合わされています。

>>> x = IndexedSet(list(range(4)) + list(range(8))) 
>>> x 
IndexedSet([0, 1, 2, 3, 4, 5, 6, 7]) 
>>> x - set(range(2)) 
IndexedSet([2, 3, 4, 5, 6, 7]) 
>>> x[-1] 
7 
>>> fcr = IndexedSet('freecreditreport.com') 
>>> ''.join(fcr[:fcr.index('.')]) 
'frecditpo' 

weakref部分はあなたがおそらく(モジュールは、スタンドアロンの、純粋なPythonの、および互換性の2/3である)相続やコードのコピーの直接的な修飾を介してそれを追加することができます重要な場合。

関連する問題