Python set()。issubset()没有按预期工作

时间:2012-10-19 17:49:00

标签: python python-2.7 set

我正在尝试使用set().issubset()来比较序列。你可以想象,它没有像预期的那样工作;)提前:抱歉长代码blob。

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

我在这里缺少什么?

1 个答案:

答案 0 :(得分:9)

您的id(您没有覆盖__hash__)正在对您的对象进行哈希处理。当然,它们不是子集,因为xxyy包含唯一对象。

为了做到这一点,你需要提出某种__hash__功能。 __hash__应始终为对象返回相同的值,这就是为什么通常会理解您不会改变可哈希对象的原因。例如,一个选择可能是:

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

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

    #... </snip>

了解到self.value无法在对象的生命周期中发生变化。 (注意,我并不认为这是一个不错的选择。您使用的实际哈希实际上取决于您的实际应用


现在为什么 - 设置(和dicts)背后的魔力和惊人的表现是他们依赖哈希。基本上,每个可清洗的对象都会变成一个“独特的”(在一个完美的世界中)数字。 Python接受“唯一”数字并将其转换为可用于获取对象句柄的数组索引(这里的魔术有点难以解释,但对于此讨论并不重要)。因此,不是通过将对象与“数组”中的所有其他对象(通常称为表)进行比较来寻找对象 - 这是一项昂贵的操作,而是根据哈希值(便宜)确切知道在哪里查找对象。默认情况下,用户定义的对象按其id(内存地址)进行哈希处理。当你创建集合xx时,python会查看每个对象id并根据它们的ID将它们放入。现在当你执行xx.issubset(yy)时,python会查看xx中的所有ID,并检查它们是否全部在yy中。但是它们都不在yy 中,因为它们都是唯一的对象(因此具有唯一的哈希值)。

但你说,“为什么xx-(yy-xx) == xx有效?”好问题。让我们分开。

首先,我们有(yy - xx)。这将返回空集,因为xx中的任何元素都不在yy中(它们都散列为不同的值,因为它们都具有唯一的id s) 。所以你真的在做

xx - set([]) == xx

这应该是非常明显的原因True