【问题标题】:Creating a namedtuple with a custom hash function使用自定义哈希函数创建命名元组
【发布时间】:2010-07-11 13:37:05
【问题描述】:

假设我有一个这样的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

那么,我该怎么办?或者这完全是个坏主意,我应该从头开始编写自己的课程?

【问题讨论】:

    标签: python inheritance overriding tuples


    【解决方案1】:

    从 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)
    

    【讨论】:

      【解决方案2】:

      我认为你的代码有问题(我的猜测是你创建了一个同名的元组实例,所以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
      

      【讨论】:

      • 请注意,repr(foo) 仍然是 Foo。这可以做得更好class Foo(namedtuple('Foo', ['item1', 'item2'], verbose=False)):
      • 关注@Sven的回答here
      【解决方案3】:

      带有自定义__hash__ 函数的namedtuple 可用于将immutable data models 存储到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
      

      【讨论】:

        猜你喜欢
        • 2010-10-22
        • 1970-01-01
        • 1970-01-01
        • 2019-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-23
        • 1970-01-01
        相关资源
        最近更新 更多