【问题标题】:How detect hashable types with structural pattern matching?如何使用结构模式匹配检测可散列类型?
【发布时间】:2021-12-19 06:01:56
【问题描述】:

使用结构模式匹配,如何编写匹配可散列类型实例的案例?

我试过了:

for obj in [], (), set(), frozenset(), 10, None, dict():
    match obj:
        case object(__hash__=_):
            print('Hashable type:  ', type(obj))
        case _:
            print('Unhashable type: ', type(obj))

但是,这得到了错误的答案,因为每种类型都定义了 __hash__ 是否可散列:

Hashable type:   <class 'list'>
Hashable type:   <class 'tuple'>
Hashable type:   <class 'set'>
Hashable type:   <class 'frozenset'>
Hashable type:   <class 'int'>
Hashable type:   <class 'NoneType'>
Hashable type:   <class 'dict'>

【问题讨论】:

    标签: python protocols hashable hasattr structural-pattern-matching


    【解决方案1】:

    Raymond Hettinger 的答案在有限的情况下有效,但它在 list(类型对象本身)这样的输入上失败,即使 list.__hash__ is None 也是可散列的,而像 ([1, 2], [3, 4]) 这样的输入是不可散列的,即使 @987654324 @。

    检测对象是否可散列的最可靠方法总是尝试散列它。如果你想在match 语句中这样做,最简单的方法是编写一个守卫:

    def hashable(x):
        try:
            hash(x)
        except TypeError:
            return False
        else:
            return True
    
    match x:
        case _ if hashable(x):
            ...
        ...
    

    这只是直接调用hash(x)并查看它是否有效,而不是尝试执行结构检查。

    如果需要哈希,又想避免重复计算,可以保存hash(x)结果:

    def try_hash(x):
        try:
            return hash(x)
        except TypeError:
            return None
    
    match x:
        case _ if (x_hash := try_hash(x)) is not None:
            ...
        ...
    

    【讨论】:

      【解决方案2】:

      解决方案

      collections.abc 中的Hashable 抽象基类可以识别使用isinstance(obj, Hashable)issubclass(cls, Hashable) 等测试实现散列的类型。

      根据PEP 622,对于class pattern,“匹配是否成功由等效的isinstance调用决定。”

      所以,你可以直接在类模式中使用Hashable

      from collections.abc import Hashable
      
      for obj in [], (), set(), frozenset(), 10, None, dict():
          match obj:
              case Hashable():
                  print('Hashable type:  ', type(obj))
              case _:
                  print('Unhashable type:', type(obj))
      

      这会产生所需的答案:

      Unhashable type: <class 'list'>
      Hashable type:   <class 'tuple'>
      Unhashable type: <class 'set'>
      Hashable type:   <class 'frozenset'>
      Hashable type:   <class 'int'>
      Hashable type:   <class 'NoneType'>
      Unhashable type: <class 'dict'>
      

      对 hash() 的调用可能仍然失败或无用

      Hashable 只处理最外层对象的类型。它在“对象的类型实现散列”的意义上报告散列性,这就是我们通常所说的“元组是可散列的”的意思。这也是抽象基类和静态类型使用的相同含义。

      虽然Hashable检测了一个类型是否实现_hash_,但它无法知道散列实际上做了什么,是否会成功,或者是否会给出一致的结果。

      例如,对于float('NaN'),散列会给出不一致的结果。元组和冻结集通常是可散列的,但如果它们的组件值不可散列,则无法散列。一个类可以定义__hash__ 总是引发异常。

      【讨论】:

      • 这会将([1, 2], [3, 4]) 视为可散列的。检查某事物是否可散列的唯一真正可靠的方法是尝试散列它。
      • 它还在对象而不是对象类型上查找__hash__,所以它认为像list这样的东西是不可散列的,因为list.__hash__None,即使type(list).__hash__不是' t None.
      • “我们在这里检测的是一个类是渴望成为可散列的还是不可散列的。” - 但问题是如何检测 特定对象 是否可散列,而不是其类型是否具有__hash__ 方法。在很多情况下,“这个对象的类型有一个__hash__ 方法”比“这个对象可以成功地散列”更合适。
      猜你喜欢
      • 2016-02-08
      • 1970-01-01
      • 2022-12-19
      • 2016-09-22
      • 1970-01-01
      • 2021-08-03
      • 2017-06-17
      • 1970-01-01
      • 2019-09-16
      相关资源
      最近更新 更多