21

Pythonセットの要素と同等に比較されるオブジェクトがあるが、同じオブジェクトではない場合、セット内のオブジェクトへの参照を取得するための合理的な方法はありますか?ユースケースは、セットを使用して重複データを識別および共有することです。

例(Python 2.7):

>>> a = "This is a string"
>>> b = "This is a string"
>>> a is b
False
>>> a == b
True
>>> s = set((a,))
>>> b in s
True

とのa使用に関するリファレンスを取得する方法は?私は1つの方法を考えることができますが、それが実装に依存していないかどうかはわかりません。編集:これは、sに複数の要素がある場合は機能しません。交差点は非常に自然に実装されていますbsab[x for x in smaller_set if x in larger_set]

>>> for x in set((b,)).intersection(s): c = x
...
>>> c is a
True

おそらく、適切な回避策は、セットではなく、各キーをそれ自体にマップするdictを使用することです。

4

2 に答える 2

5

python-list: Get item from setで同様の質問を見つけました。get_equivalent(container, item) (Python レシピ)を参照した賢い答えがあります。

トリックは、「キー」オブジェクトのラッパー オブジェクトを構築し、in演算子を使用してラッパーがセット内にあるかどうかを確認することです。ラッパーのハッシュ値がキーと等しい場合、その__eq__メソッドはセット内のオブジェクトにアクセスし、そのオブジェクトへの参照を保存できます。議論の重要な点は__eq__、set 要素のメソッドはNotImplemented認識されない型に対して返さなければならないということです。そうしないと、ラッパーのメソッド__eq__が呼び出されない可能性があります。

于 2011-12-29T20:32:41.120 に答える
3

あなたのユースケースは、辞書のユースケースのように聞こえます。「外部」オブジェクトと等しいオブジェクトの属性をキーとして使用し、目的のオブジェクト自体を値として使用します。

それが単純なユースケースであり、線形検索を行うことができる場合は、明らかなことを行うことができます-それは悪くありません:

def get_equal(in_set, in_element):
   for element in in_set:
       if element == in_element:
           return element
   return None 

あなたが求めているものが正確に必要な場合(そのためのユースケースがいくつかあると思います)-行く方法は、メンバーの1つとしてセットを持つカスタム辞書クラスを作成することです。メンバーセットにプロキシメソッドを実装します、およびディクショナリとセットの両方のメソッドで、ディクショナリとセットの両方のコンテンツの同期を維持します。これを正しく実装するには時間がかかりますが、比較的簡単で、O(1) 時間かかります。

すべてのデータへの参照をコピーする必要がない場合 (これは線形ですが、上記の単純な検索よりもおそらく最悪です)、次の式を使用できます。

(data - (data - {key})).pop()

次のように:

In [40]: class A:
    ...:     def __init__(self, id, extra):
    ...:         self.id = id
    ...:         self.extra = extra
    ...:     def __eq__(self, other):
    ...:         return self.id == other.id
    ...:     def __hash__(self):
    ...:         return hash(self.id)
    ...:     def __repr__(self):
    ...:         return f"({self.id}, {self.extra})"
    ...: 
    ...: 

In [41]: data = set(A(i, "initial") for i in range(10))

In [42]: (data - (data - {A(5, None)})).pop()
Out[42]: (5, initial)
于 2011-12-23T13:39:19.057 に答える