关于python:lru_cache会干扰single_dispatch完成的类型检查

lru_cache interferes with type checking done by single_dispatch

我有一个方法分派装饰器,它有三个注册函数。一个发到int上,效果很好。第二个在自定义类型上调度,也可以正常工作。第三个也是自定义类型,但类是用lru_cache修饰器包装的。

(为了使事情稍微复杂一点,类是通过另一个类的__call__方法上的methodDispatch以迂回的方式实例化的。)

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

在球场等级内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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缓存,并将一个限定的interval传递给函数,那么它不会分派并引发notimplementederror。如果我注释掉缓存装饰器,它就会工作。而repl上的手动类型检查则以任何方式显示相同的类型("qualifiedinterval")。我尝试了几种不同的方法调用创建限定interval的命令,并尝试将其分配给一个变量。还是不行。我尝试在增广函数中执行显式类型检查。如果启用了缓存,类型检查也会在那里失败。


分析出了什么问题

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

lru缓存是返回包装任何可调用(包括类)的修饰符的函数。因此,当您将lru缓存应用于QualifiedInterval类时,该变量将被分配给包装函数,而不是类本身。

1
2
3
4
5
6
>>> @lru_cache(maxsize=None, typed=True)
class QualifiedInterval:
    pass

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

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

解决方案

教单个分派以匹配实际的原始类(类型),而不是包装类:

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

注意添加了.__wrapped__属性,该属性通过包装函数到达原始的未包装类。

希望能把一切都弄清楚,并显示出前进的方向。