假设我有这样的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中的子类化和初始化,特别是对于元组。这个解决方案有什么缺陷吗?有推荐的方法怎么做?好吗?提前致谢。
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
。
问题中的代码可以受益于__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。
在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))
,这可能不是预期的。
+注意,名称“unsafe”表示尽管对象是可变的,但仍将使用默认哈希。这可能是不希望的,特别是在期望不可变密钥的字典中。可以使用适当的关键字打开不可变哈希值。另请参阅有关数据类和hashing logic中的related issue的更多信息。