假设我有namedtuple
这样:
EdgeBase = namedtuple("EdgeBase", "left, right")
我想为此实现一个自定义哈希函数,所以我创建了以下子类:
class Edge(EdgeBase):
def __hash__(self):
return hash(self.left) * hash(self.right)
由于对象是不可变的,我希望哈希值只计算一次,所以我这样做:
class Edge(EdgeBase):
def __init__(self, left, right):
self._hash = hash(self.left) * hash(self.right)
def __hash__(self):
return self._hash
这似乎有效,但我真的不确定Python中的子类化和初始化,特别是使用元组。这个解决方案有什么缺陷吗?有推荐的方法怎么做?好吗?提前谢谢。
答案 0 :(得分:49)
2017年编辑: turns out namedtuple
isn't a great idea。 attrs是现代的选择。
class Edge(EdgeBase):
def __new__(cls, left, right):
self = super(Edge, cls).__new__(cls, left, right)
self._hash = hash(self.left) * hash(self.right)
return self
def __hash__(self):
return self._hash
__new__
是你想在这里调用的,因为元组是不可变的。不可变对象在__new__
中创建,然后返回给用户,而不是填充__init__
中的数据。
cls
两次传递给super
上的__new__
来电,因为__new__
由于历史/奇怪的原因而隐含staticmethod
。
答案 1 :(得分:3)
问题中的代码可以从__init__
中的超级调用中受益,以防它在多重继承情况下被子类化,但是否则是正确的。
class Edge(EdgeBase):
def __init__(self, left, right):
super(Edge, self).__init__(left, right)
self._hash = hash(self.left) * hash(self.right)
def __hash__(self):
return self._hash
虽然元组只读取它们的子类的元组部分是只读的,但其他属性可以像往常一样编写,这样就可以将赋值分配给_hash,无论它是在__init__
还是__new__
中完成的。您可以通过将子类__slots__
设置为()来完全只读子类,这具有节省内存的额外好处,但是您将无法分配给_hash。
答案 2 :(得分:0)
在Python 3.7+中,您现在可以使用dataclasses轻松构建可哈希化的类。
代码
假设int
和left
的类型为right
,我们通过unsafe_hash
+ 关键字使用默认哈希:
import dataclasses as dc
@dc.dataclass(unsafe_hash=True)
class Edge:
left: int
right: int
hash(Edge(1, 2))
# 3713081631934410656
现在,我们可以将这些(可变)可哈希对象用作集合中的元素或(字典中的键)。
{Edge(1, 2), Edge(1, 2), Edge(2, 1), Edge(2, 3)}
# {Edge(left=1, right=2), Edge(left=2, right=1), Edge(left=2, right=3)}
详细信息
我们也可以覆盖__hash__
函数:
@dc.dataclass
class Edge:
left: int
right: int
def __post_init__(self):
# Add custom hashing function here
self._hash = hash((self.left, self.right)) # emulates default
def __hash__(self):
return self._hash
hash(Edge(1, 2))
# 3713081631934410656
扩展@ShadowRanger的注释,OP的自定义哈希函数不可靠。特别地,属性值可以互换,例如。 hash(Edge(1, 2)) == hash(Edge(2, 1))
,这可能不是预期的。
+ 注意,名称“不安全”表示尽管对象可变,但仍将使用默认哈希。这可能是不希望的,尤其是在期待不可变键的字典中。可以使用适当的关键字打开不可变的哈希。另请参见数据类中的hashing logic和related issue。