2

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

我试过了:

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'>
4

2 回答 2

3

解决方案

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

根据PEP 622,对于类模式,“匹配是否成功取决于 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__为总是引发异常。

于 2021-11-05T05:06:15.160 回答
3

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

检测对象是否可散列的最可靠方法总是尝试散列它。如果你想在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:
        ...
    ...
于 2021-11-05T05:41:12.150 回答