将集转换为可哈希的类型

时间:2019-04-17 15:44:11

标签: python oop hash

我有一些变量被两个两个相关,我希望将它们作为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”。 有关如何解决此问题的任何建议?

3 个答案:

答案 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'})