我有一些变量被两个两个相关,我希望将它们作为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,而是添加一个新的键值对。通过定义哈希的方式可以理解这一点。
我尝试用 hash 中的集合替换向量,但出现TypeError:无法哈希的类型:“ set”。 有关如何解决此问题的任何建议?
答案 0 :(得分:2)
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})
答案 1 :(得分:2)
使用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
请注意,哈希不得在对象的时间轴内更改。只要您在初始化后不写_brother
和_sister
属性,就应该很安全。
答案 2 :(得分:1)
正如其他答案已经指出的那样,使用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'})