lru_cache 干扰了 single_dispatch 完成的类型检查

lru_cache interferes with type checking done by single_dispatch

我有一个 method dispatch decorator 具有三个注册函数。一个在 int 上调度,效果很好。第二个按自定义类型分派,也可以正常工作。第三个也是自定义类型,但是 Class 被 lru_cache 装饰器包裹。

(为了让事情更复杂一点,class 是通过另一个 class 的 __call__ 方法上的 methoddispatch 以迂回方式实例化的。)

@lru_cache(maxsize=None, typed=True)
class QualifiedInterval:
    # stuff that works

球场内部 class:

@oph_utils.method_dispatch
def augmented(self, other):
    raise NotImplementedError

@augmented.register(int)
def _(self, other):
    return "works fine"


@augmented.register(Interval)
def _(self, other):
    return "works fine too"

@augmented.register(QualifiedInterval)
def _(self, other):
    return "only works if QualifiedInterval class does NOT have lru_cache"

(还有很多事情要做,但这是不起作用的部分。)

基本上 - 如果我有 lru_cache,并将 QualifiedInterval 传递给函数,它不会调度并引发 NotImplementedError。如果我注释掉缓存装饰器,它就可以工作。 REPL 的手动类型检查显示相同的类型 ("QualifiedInterval")。我试过用几种不同的方式调用创建 QualifiedInterval 的命令,并尝试将它分配给一个变量。还是不行。我试过在 Augmented 函数中进行显式类型检查。如果启用缓存,类型检查也会在那里失败。

问题分析

Basically - if I have lru_cache, and pass a QualifiedInterval into the function, it does not dispatch

包装任何可调用对象(包括 classes)的 lru_cache is function that returns a decorator。因此,当您将 lru_cache 应用于 QualifiedInterval class 时,该变量将分配给包装函数而不是class本身。

>>> @lru_cache(maxsize=None, typed=True)
class QualifiedInterval:
    pass

>>> type(QualifiedInterval)
<class 'functools._lru_cache_wrapper'>

Single dispatch 通过将第一个参数的类型与适当的方法匹配来工作。但是,当您传入 QualifiedInterval 的实例时,它的类型与 functools._lru_cache_wrapper 不匹配,因此单个调度会回退到基本方法(引发 NotImplemented.

解决方案

教导单个调度匹配实际的原始 class(类型)而不是包装的 class:

@augmented.register(QualifiedInterval.__wrapped__)
def _(self, other):
    return "show now work QualifiedInterval class has an lru_cache"

请注意 .__wrapped__ 属性的添加,它通过包装函数到达原始未包装的 class。

希望一切都过去了,并指明了前进的方向:-)