我需要从函数添加属性,但找不到类型提示支持它的方法。
尝试使用协议联合表示返回类型时会出现问题。
from typing import Any, Protocol, TypeVar, Union
class JsonDictBase(Protocol): ...
class JsonDict(JsonDictBase, Protocol):
def __init__(self, **kwargs: Any) -> None: ...
key: str
value: int
other: Any
T = TypeVar('T', bound=JsonDictBase)
def build(cls: Union[T, type[JsonDictBase]]) -> Union[T, type[JsonDict]]:
...
@build
class MyClass:
name: str
cls = MyClass # type[MyClass] | type[JsonDict]
instance = MyClass(key='as') # fails
我期望
build
函数做的是将 JsonDict 协议属性添加到输入的类中。不使用继承
@build
class MyClass:
name: str
# So python understands this
class MyClass:
name: str
def __init__(self, **kwargs: Any) -> None: ...
key: str
value: int
other: Any
您在这里要做的是使用
build
创建 MyClass
和 JsonDict
的交集类型。交集类型与并集类型相反。 Union[A, B]
表示为 A
或 B
的类型。 Intersection[A, B]
表示A
和B
的类型。遗憾的是 python 还不支持交集类型 - 不过它已经在工作了。
Pyright 内部确实支持交叉类型 - 有一个解决方法,但遗憾的是仅使用该装饰器是不可能的。其他类型检查器也可能支持该模式,但我不知道这些。
from typing import TYPE_CHECKING, Any, Protocol, TypeVar
class JsonDictBase(Protocol): ...
class JsonDict(JsonDictBase, Protocol):
def __init__(self, **kwargs: Any) -> None: ...
key: str
value: int
other: Any
T = TypeVar('T', bound=type[JsonDictBase])
def build(cls: T) -> T: # note 'build' doesn't do anything regarding typing
...
@build
class MyClass:
name: str
if TYPE_CHECKING:
# afaik there's no way to avoid this assert if you want an intersection
assert issubclass(MyClass, JsonDict)
cls = MyClass # type[<subclass of MyClass and JsonDict>]
instance = MyClass(key='as') # succeeds
instance.name
instance.value
注意:由于交叉类型不是一个合适的功能,它们有时表现得有点古怪。您可能会收到漏报和令人困惑的错误消息,因此请做好准备。