我有一些两个相关的变量,我希望将它们作为Siblings()对象的属性存储。所以,我创建了一个类:
def Siblings(object):
def __init__(self, brother, sister):
self._brother = brother
self._sister = sister
def __eq__(self, other):
if isinstance(other, Siblings):
return {self._brother, self._sister} == {other._brother, other._sister}
def __hash__(self):
return hash((self._brother, self._sister)
我在eq中使用集合的原因是我不知道(并且不关心)哪个变量将被视为兄弟,哪个变量将被视为姐妹。事实上,
s1 = Siblings(1,2)
s2 = Siblings(2,1)
print(s1 == s2)
打印True,这是我需要的。问题是我的Siblings()对象需要用作字典键,因此上面提到的s1和s2对应于相同的键。但是:
a = {s1: 5}
a[s2] = 4
不会像我想的那样修改值5,而是添加一个新的键值对。我的哈希定义方式可以理解这一点。
我尝试用散列中的set替换向量,但我得到一个TypeError:unhashable类型:'set'。关于如何解决这个问题的任何建议?
用frozenset
而不是元组哈希:
def __hash__(self):
return hash(frozenset([self._brother, self._sister]))
与问题无关,但建议 - 你应该避免在None
返回__eq__
:
def __eq__(self, other):
if isinstance(other, Siblings):
return {self._brother, self._sister} == {other._brother, other._sister}
return NotImplemented
请注意,散列不得在对象的时间轴内更改。只要你在init之后没有写入_brother
和_sister
属性,你应该是安全的。
Python有一个名为frozenset
的可嵌入集合。它是不可变的,所以如果你确实需要更改它,你必须创建一个新对象(就像使用元组一样)。
a = frozenset([1, 2])
b = frozenset([2, 1])
a == b
# True
d = {a: "a"}
# {frozenset({1, 2}): 'a'}
d[b] = "b"
# {frozenset({1, 2}): 'b'}
a.add(3)
# AttributeError: 'frozenset' object has no attribute 'add'
a = a | {3}
# frozenset({1, 2, 3})
正如其他答案已经指出的那样,使用frozenset是一种有效的方法,对于这些类型的案例另一个有用的选择是考虑使用namedtuples,例如:
from collections import namedtuple
BaseSiblings = namedtuple('Siblings', 'brother sister')
class Siblings(BaseSiblings):
def __hash__(self):
return super().__hash__()
def __eq__(self, other):
if isinstance(other, Siblings):
return {self.brother, self.sister} == {other.brother, other.sister}
return NotImplemented
obj1 = Siblings('a', 'b')
print(obj1)
obj2 = Siblings('b', 'a')
print(obj2 == obj1)
print({obj1: '1'} == {obj2: '1'})