python set.__contains__ 的意外行为
借用一下__contains__
的文档内容
print set.__contains__.__doc__
x.__contains__(y) <==> y in x.
对于像整数(int)、基本字符串(basestring)这样的基本对象,这个方法似乎运行得很好。但是对于那些自己定义的对象,如果这些对象定义了__ne__
和__eq__
这两个方法,我就遇到了意想不到的情况。下面是一个示例代码:
class CA(object):
def __init__(self,name):
self.name = name
def __eq__(self,other):
if self.name == other.name:
return True
return False
def __ne__(self,other):
return not self.__eq__(other)
obj1 = CA('hello')
obj2 = CA('hello')
theList = [obj1,]
theSet = set(theList)
# Test 1: list
print (obj2 in theList) # return True
# Test 2: set weird
print (obj2 in theSet) # return False unexpected
# Test 3: iterating over the set
found = False
for x in theSet:
if x == obj2:
found = True
print found # return True
# Test 4: Typcasting the set to a list
print (obj2 in list(theSet)) # return True
那么这算是一个错误,还是一个特性呢?
3 个回答
2
一个 set
会对它的元素进行哈希处理,这样可以快速查找。你需要重写 __hash__
方法,这样才能找到某个元素:
class CA(object):
def __hash__(self):
return hash(self.name)
而列表则不使用哈希,它会像你在 for
循环中那样逐个比较每个元素。
3
7
对于 set
(集合)和 dict
(字典),你需要定义一个叫做 __hash__
的方法。任何两个相等的对象,它们的哈希值也应该是相同的,这样在 set
和 dict
中才能得到一致和预期的行为。
我建议使用一个 _key
方法,然后在需要比较某个项目的部分时直接引用它,就像你在 __ne__
方法中调用 __eq__
一样,而不是重新实现它:
class CA(object):
def __init__(self,name):
self.name = name
def _key(self):
return type(self), self.name
def __hash__(self):
return hash(self._key())
def __eq__(self,other):
if self._key() == other._key():
return True
return False
def __ne__(self,other):
return not self.__eq__(other)