将set转换为hashable类型

问题描述 投票:0回答:3

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

python oop hash
3个回答
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

请注意,散列不得在对象的时间轴内更改。只要你在init之后没有写入_brother_sister属性,你应该是安全的。


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
投票

正如其他答案已经指出的那样,使用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'})
© www.soinside.com 2019 - 2024. All rights reserved.