用自定义哈希函数创建命名元组

29 投票
3 回答
18092 浏览
提问于 2025-04-16 01:07

假设我有一个叫做 namedtuple 的东西,长这样:

FooTuple = namedtuple("FooTuple", "item1, item2")

我想用下面这个函数来进行哈希处理:

foo_hash(self):
    return hash(self.item1) * (self.item2)

我这么做是因为我希望 item1item2 的顺序不重要(我也会对比较操作做同样的处理)。我想到了两种方法来实现这个。第一种方法是:

FooTuple.__hash__ = foo_hash

这个方法可以用,但我觉得有点不太正规。所以我尝试去继承 FooTuple

class EnhancedFooTuple(FooTuple):
    def __init__(self, item1, item2):
        FooTuple.__init__(self, item1, item2)

    # custom hash function here

但是我遇到了这个问题:

DeprecationWarning: object.__init__() takes no parameters

那么,我该怎么办呢?或者说这根本就是个坏主意,我应该从头开始写自己的类吗?

3 个回答

0

一个带有自定义 namedtuple__hash__ 函数很有用,可以把 不可变的数据模型 存储到 dictset 中。

举个例子:

class Point(namedtuple('Point', ['label', 'lat', 'lng'])):
    def __eq__(self, other):
        return self.label == other.label

    def __hash__(self):
        return hash(self.label)

    def __str__(self):
        return ", ".join([str(self.lat), str(self.lng)])

重写 __eq____hash__ 两个方法,可以把不同的业务分组到一个 set 里,确保每个业务线在这个集合中都是唯一的:

walgreens = Point(label='Drugstore', lat = 37.78735890, lng = -122.40822700)
mcdonalds = Point(label='Restaurant', lat = 37.78735890, lng = -122.40822700)
pizza_hut = Point(label='Restaurant', lat = 37.78735881, lng = -122.40822713)

businesses = [walgreens, mcdonalds, pizza_hut]
businesses_by_line = set(businesses)

assert len(business) == 3
assert len(businesses_by_line) == 2
19

从Python 3.6.1开始,我们可以使用typing.NamedTuple这个类来更简单地实现这个功能(只要你对类型提示没有意见):

from typing import NamedTuple, Any


class FooTuple(NamedTuple):
    item1: Any
    item2: Any

    def __hash__(self):
        return hash(self.item1) * hash(self.item2)
34

我觉得你的代码有点问题(我猜是你创建了一个和元组同名的实例,所以现在fooTuple变成了一个元组,而不是元组的类),因为像这样继承命名元组应该是可以的。无论如何,你不需要重新定义构造函数。你只需要添加哈希函数就可以了:

In [1]: from collections import namedtuple

In [2]: Foo = namedtuple('Foo', ['item1', 'item2'], verbose=False)

In [3]: class ExtendedFoo(Foo):
   ...:     def __hash__(self):
   ...:         return hash(self.item1) * hash(self.item2)
   ...: 

In [4]: foo = ExtendedFoo(1, 2)

In [5]: hash(foo)
Out[5]: 2

撰写回答