5

set().issubset()シーケンスの比較に使用しようとしています。ご想像のとおり、期待どおりに動作していません ;) 前もって: 長いコード ブロブで申し訳ありません。

class T(object):
  def __init__(self, value, attributes = None):
    self.value = value
    self.attributes = Attributes(attributes)

  def __eq__(self, other):
    if not isinstance(other, T):
      return False
    if self.value == other.value and self.attributes == other.attributes:
      return True
    else:
      return False

  def __ne__(self, other):
    if not isinstance(other, T):
      return True
    if self.value != other.value or self.attributes != other.attributes:
      return True
    else:
      return False

class Attributes(dict):
  def __init__(self, attributes):
    super(dict, self)
    self.update(attributes or dict())

  def __eq__(self, other):
    if self.items() == other.items():
      return True
    else:
      return False

  def __ne__(self, other):
    if not self.items() == other.items():
      return True
    else:
      return False

  def __cmp__(self, other):
    return self.items().__cmp__(other.items())


x = [T("I", {'pos': 0}), T("am", {'pos': 1}), T("a", {'pos': 2}), T("test", {'pos': 3})]
y = [T("a", {'pos': 2}), T("test", {'pos': 3})] 
xx = set(x)
yy = set(y)

assert y[0] == x[2], "__eq__ did fail, really?" #works
assert y[1] == x[3], "__eq__ did fail, really?" #works
assert xx-(yy-xx) == xx, "set subtract not working" #works, but is nonsense. see accepted answer
assert not xx.issubset(yy), "i am doing it wrong..." #works
assert yy.issubset(xx), "issubset not working :(" #FAILS!

上記のコードを実行すると、最後のアサーションが失敗します。

$ python
Python 2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import issubsettest
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "issubsettest.py", line 52, in <module>
    assert yy.issubset(xx), "issubset not working :("
AssertionError: issubset not working :(
>>> 

ここで何が欠けていますか?

4

1 に答える 1

9

あなたのオブジェクトはそれらによってハッシュされてidいます(あなたはオーバーライドしませんでした__hash__)。もちろん、それらはサブセットではなく、固有のオブジェクトxxを含んでいます。yy

__hash__これを行うには、何らかの関数 を考え出す必要があります。__hash__オブジェクトに対して常に同じ値を返す必要があるため、ハッシュ可能なオブジェクトを変更しないことが通常理解されています。たとえば、次のような選択肢があります。

class T(object):
    #<snip> ...

    def __hash__(self):
        return hash(self.value)

    #... </snip>

self.valueオブジェクトの寿命のために変更できないという理解を持っています。(注意してください、私はそれが良い選択であるとは主張していません。使用する実際のハッシュは、実際のアプリケーションに本当に依存しています)


その理由 - セット (および辞書) の背後にある魔法と驚くべきパフォーマンスは、それらがハッシュに依存していることです。基本的に、すべてのハッシュ可能なオブジェクトは「一意の」(完全な世界での) 番号に変換されます。Python はその「一意の」番号を取得し、それをオブジェクトのハンドルを取得するために使用できる配列インデックスに変換します (ここでの魔法は説明が少し難しいですが、この議論では重要ではありません)。したがって、「配列」(通常はテーブルと呼ばれる)内の他のすべてのオブジェクトと比較してオブジェクトを探すのではなく、高価な操作であり、ハッシュ値に基づいてオブジェクトを探す場所を正確に知っています(安価) . デフォルトでは、ユーザー定義オブジェクトはそのid(メモリ アドレス) によってハッシュされます。set を作成するとxx、python は各オブジェクトを調べますids を作成し、ID に基づいてそれらを に配置します。これxx.issubset(yy)で、python は 内のすべての IDxxを調べて、それらがすべて 内にあるかどうかを確認しますyy。しかし、それらはすべて一意のオブジェクトであるため (したがって、一意のハッシュ値を持っているため)、どれも含まれていません。yy

しかし、あなたは「なぜうまくいったのxx-(yy-xx) == xxですか?」と言います。良い質問。これを引き離しましょう。

まず、 があり(yy - xx)ます。これは空のセットを返します。ここでも、 の要素が含まれxxていないためですyy(それらはすべて一意idの s を持っているため、すべて異なる値にハッシュされます)。だからあなたは本当にやっている

xx - set([]) == xx

それがなぜなのかは明らかですTrue

于 2012-10-19T17:58:14.253 に答える