如何使用结构模式匹配检测可散列类型?
How detect hashable types with structural pattern matching?
使用结构模式匹配,如何编写匹配可散列类型实例的案例?
我试过:
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'>
解决方案
Hashable abstract base class in collections.abc 可以识别使用 isinstance(obj, Hashable)
或 issubclass(cls, Hashable)
等测试实现散列的类型。
根据 PEP 622, for a class pattern,“匹配是否成功取决于 isinstance 调用的等价物。”
因此,您可以在 class 模式中直接使用 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只处理最外层对象的类型。它在“对象的类型实现散列”的意义上报告可散列性,这就是我们通常所说的“元组是可散列的”的意思。这也是抽象基础 classes 和静态类型所使用的相同含义。
虽然 Hashable 检测类型是否实现 _hash_,但它无法知道哈希实际上做了什么,是否会成功,或者是否会给出一致的结果。
例如,散列法给出 float('NaN')
的不一致结果。元组和冻结集通常是可散列的,但如果它们的组件值不可散列,则将无法散列。 class 可以定义 __hash__
以始终引发异常。
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又想避免重复计算,可以保存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:
...
...
使用结构模式匹配,如何编写匹配可散列类型实例的案例?
我试过:
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'>
解决方案
Hashable abstract base class in collections.abc 可以识别使用 isinstance(obj, Hashable)
或 issubclass(cls, Hashable)
等测试实现散列的类型。
根据 PEP 622, for a class pattern,“匹配是否成功取决于 isinstance 调用的等价物。”
因此,您可以在 class 模式中直接使用 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只处理最外层对象的类型。它在“对象的类型实现散列”的意义上报告可散列性,这就是我们通常所说的“元组是可散列的”的意思。这也是抽象基础 classes 和静态类型所使用的相同含义。
虽然 Hashable 检测类型是否实现 _hash_,但它无法知道哈希实际上做了什么,是否会成功,或者是否会给出一致的结果。
例如,散列法给出 float('NaN')
的不一致结果。元组和冻结集通常是可散列的,但如果它们的组件值不可散列,则将无法散列。 class 可以定义 __hash__
以始终引发异常。
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又想避免重复计算,可以保存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:
...
...