【发布时间】:2021-11-18 15:29:01
【问题描述】:
假设我们有一些函数 func 将类 A 的实例映射到类 B 的实例,即它具有签名 Callable[[A], B]。
我想为A 的子类编写一个类装饰器autofunc,它会在创建实例时自动将func 应用于实例。例如,考虑基于全局环境变量的自动 jit 编译。这可以通过
from functools import wraps
def autofunc(basecls):
@wraps(basecls, updated=())
class WrappedClass(basecls):
def __new__(cls, *args, **kwargs):
instance = basecls(*args, **kwargs)
return func(instance)
return WrappedClass
那么下面两个大致等价:
<b>class</b> C(A): ... instance = func(C()) |
@autofunc <b>class</b> C(A): ... instance = C() |
|---|
天真的我试过了
def autofunc(basecls: type[A]) -> type[B]:
@wraps(basecls, updated=())
class WrappedClass(basecls):
def __new__(cls, *args, **kwargs):
instance = basecls(*args, **kwargs)
return func(instance)
return WrappedClass
mypy 真的很不喜欢,引发错误:
error: Variable "basecls" is not valid as a type [valid-type]error: Invalid base class "basecls" [misc]- 另外,
WrapperClass.__new__返回的实例是B而不是WrapperClass。
目前有什么方法可以正确键入提示这样的类装饰器,或者mypy 还不能工作吗?
示例代码:
from functools import wraps
class A:
pass
class B:
pass
def func(cl: A) -> B:
print(f"Replacing {cl=}")
return B()
def autofunc(basecls: type[A]) -> type[B]:
@wraps(basecls, updated=())
class WrappedClass(basecls):
def __new__(cls, *args, **kwargs):
instance = basecls()
return func(instance)
return WrappedClass
【问题讨论】:
-
怀疑这会解决问题,但不应该是
type[A] -> type[A]吗? -
@joel 我不确定。问题在于,就目前的编写方式而言,
WrapperClass本身的定义并不明确,因为它是basecls的子类,即type[A],但__new__构造函数返回type[B]的对象。MyPy也理所当然地抱怨这一点,但我还没有找到更好的选择。我写type[A] -> type[B]的原因是func映射A -> B。事实上,从技术上讲,autofunc确实映射了type[A] -> type[A],但在实例化之后你总是得到type[B]。 -
是的,好点。在相关说明中,如果
B不是A,则从A的__new__返回B是否有效?我原以为这真的会打破一些 OO/类型假设 -
@joel 它是有效的、可工作的 python 代码。但当然它与类和
__new__应该如何工作的心理模型相冲突。
标签: type-hinting mypy python-typing python-3.9 python-3.10