简体   繁体   English

使用结构模式匹配来检测可哈希性

[英]Using structural pattern matching to detect hashability

使用结构模式匹配,你如何编写一个匹配可哈希对象的案例?

Core problem核心问题

All classes start out by being hashable because they inherit from object which defines a __hash__() method based on the object id:所有类都是从可散列开始的,因为它们继承自对象,该对象定义了基于对象 id 的__hash__()方法:

>>> hash(object())
269091997

To become unhashable, classes have to override object and set __hash__ to None :要变得不可散列,类必须覆盖object并将__hash__设置为None

class UnhashableTuple(tuple):
    __hash__ = None

Structural pattern matching can match cases when the __hash__ attribute is None , but it doesn't provide a direct way to detect when the attribute is not None .__hash__属性为None ,结构模式匹配可以匹配情况,但它不提供直接方法来检测属性何时不为None

Solution解决方案

Build two cases.构建两个案例。 The first case filters-out unhashable instances.第一种情况过滤掉不可散列的实例。 This leaves only hashable instances for the second case.这仅为第二种情况留下了可散列的实例。

Example例子

Here we test a number of objects for hashability:在这里,我们测试了一些对象的哈希能力:

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

This outputs:这输出:

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

Containers容器

For containers, the above only detects whether the container is hashable.对于容器,上面只检测容器是否是可散列的。 It is possible that the elements in the container are unhashable.容器中的元素可能是不可散列的。

Fortunately, pattern matching can destructure an object and let you test you test the components as well as the container (using the same technique).幸运的是,模式匹配可以解构对象并让您测试组件和容器(使用相同的技术)。 This is somewhat messy though.不过这有点乱。

Raymond Hettinger's answer works in limited cases, but it fails on inputs like list , which is hashable even though list.__hash__ is None , and inputs like ([1, 2], [3, 4]) , which is unhashable even though tuple.__hash__ is not None . Raymond Hettinger 的答案在有限的情况下有效,但它在像list这样的输入上失败,即使list.__hash__ is None也是可散列的,而像([1, 2], [3, 4])这样的输入,即使是tuple.__hash__ is not None也是不可散列的tuple.__hash__ is not None

The most reliable way to detect whether an object is hashable is always going to be to try to hash it.检测对象是否可散列的最可靠方法始终是尝试对其进行散列。 If you want to do that in a case statement, the easiest way would be to write a guard:如果你想在case语句中这样做,最简单的方法是写一个守卫:

def hashable(x):
    try:
        hash(x)
    except TypeError:
        return False
    else:
        return True

match x:
    case _ if hashable(x):
        ...
    ...

This just directly calls hash(x) and sees whether it works, instead of trying to perform a structural check.这只是直接调用hash(x)并查看它是否有效,而不是尝试执行结构检查。

If you need the hash and want to avoid double-computation, you can save the hash(x) result:如果你需要散列并且想要避免双重计算,你可以保存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:
        ...
    ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM