我有以下代码:
class Mixin:
def foo(self) -> int:
return 1
def add_mixin(cls):
class WithMixin(cls, Mixin):
__name__ = cls.__name__
__doc__ = cls.__doc__
return WithMixin
@add_mixin
class B:
def bar(self) -> int:
return 2
b = B()
print(b.foo())
print(b.bar())
我明白,在这种特殊情况下,我可以只编写B(Mixin)
并避免使用装饰器,但实际上 中有很多逻辑add_mixin
,它选择/自定义Mixin
类(想想@dataclass
)。
代码按您期望的方式工作:打印 1 和 2。但是,它使我蒙蔽了双眼pyright
- 我不再可以跳转到bar
,因为pyright
不明WithMixin
白继承自Mixin
和B
。
此外,mypy
以另一种方式感到困惑:
mypy type_checking.py
type_checking.py:17: error: "B" has no attribute "foo"
Found 1 error in 1 file (checked 1 source file)
我正在注释现有库的核心,所以列表@overloads
或一些彻底的魔法对我来说很好 - 我只是希望注释是正确的。
我想在两件事上得到一些帮助:
- 我如何正确注释
add_mixin
(或重写它以产生相同的效果)以便pyright
/mypy
正常工作? - 有没有关于打字的综合资源?最好是解释
typing
模块的实现(这令人困惑!)以及类型检查器使用这些注释的方式。我已经阅读了有关 typing以及PEP 483和PEP 484的参考资料,并且我认为我对所有协变/逆变/绑定类型的东西都有很好的掌握。然而,当有一个我以前从未见过的复杂案例时(比如这里),我感到完全迷失了。谷歌搜索大多会导致相同的参考风格或表面级文章(或 PEP)。
我经历了很多失败的尝试,最后一个是:
from typing import *
class Mixin:
def foo(self) -> int:
return 1
_T1 = TypeVar("_T1")
class WithMixin(Type[_T1], Mixin):
...
def add_mixin(cls: Type[_T1]) -> WithMixin[_T1]:
return type(cls.__name__, (cls, Mixin), {})
@add_mixin
class B:
def bar(self) -> int:
return 2
b = B()
print(b.foo())
print(b.bar())
pyright
在这条线上感到困惑type(cls.__name__, (cls, Mixin), {})
,同时mypy
说不允许继承Type[_T1]
(它不是,但我认为类型检查器可能会选择它)。