Python 3.6 接受泛型的函数的类型提示 class 类型和相同泛型类型的实例类型
Python 3.6 type hinting for a function accepting generic class type and instance type of the same generic type
我有一个具有以下签名的函数:
def wait_for_namespaced_objects_condition(
obj_type: Type[NamespacedAPIObject],
obj_condition_fun: Callable[[NamespacedAPIObject], bool],
) -> List[NamespacedAPIObject]:
...
此处重要的部分是 NamespacedAPIObject
参数。此函数将 obj_type
作为类型规范,然后创建该类型 (class) 的对象(实例)。然后将该类型的一些其他对象添加到列表中,然后使用 obj_condition_fun
过滤并作为类型 List[NamespacedAPIObject]. This works fine and also evaluates OK with
mypy`.
的结果返回
现在,我想让这个函数成为通用函数,这样就可以在 NamespacedAPIObject
的地方使用它的任何子类型。我的尝试是这样做的:
T = TypeVar("T", bound=NamespacedAPIObject)
def wait_for_namespaced_objects_condition(
obj_type: Type[T],
obj_condition_fun: Callable[[T], bool],
) -> List[T]:
但是 Type[T]
是 TypeVar
,所以这不是正确的方法。问题是:obj_type
参数的类型应该是什么才能使它起作用?我试过Generic[T]
,这对我来说似乎是最合理的,但它不起作用。如果我把 obj_type: T
放在那里,mypy
会评估这个 OK,但对我来说似乎是错误的。在我看来,这意味着 obj_type
是 class 的一个实例,它是 NamespacedAPIObject
的子类型,而我想说的是“T 是一个 class 变量表示 NamespacedAPIObject
的子类型。如何解决这个问题?
[编辑]
更好地解释一下(见下面的评论)为什么 Type[T]
对我不起作用:在我的例子中,T 的所有子类型都实现 class 方法 objects()
,但是当我尝试写我的代码是这样的:
obj_type.objects()
mypy
returns:
pytest_helm_charts/utils.py:36: error: "Type[T]" has no attribute "objects"
为什么 Type[T]
不是正确的选择?在我看来,它与 examples:
之一非常相似
However using Type[] and a type variable with an upper bound we can do much better:
U = TypeVar('U', bound=User)
def new_user(user_class: Type[U]) -> U:
...
Now when we call new_user() with a specific subclass of User a type checker will infer the correct type of the result:
joe = new_user(BasicUser) # Inferred type is BasicUser
如果使用classmethod
:
from typing import Callable, Type, List, TypeVar
T = TypeVar('T', bound=Base)
class Base:
@classmethod
def objects(cls: Type[T]) -> List[T]:
...
def run(self):
...
class Derived(Base):
def run(self):
...
def foo(d: Derived) -> bool:
return True
def wait_for_namespaced_objects_condition(
obj_type: Type[T],
obj_condition_fun: Callable[[T], bool],
) -> List[T]:
a = obj_type.objects()
return a
wait_for_namespaced_objects_condition(Derived, foo)
But Type[T] is TypeVar, so it's not the way to go.
不,您走在正确的轨道上 - TypeVar
绝对是正确的选择。这里的问题在于 pykube.objects.APIObject
class 被包装在 mypy
尚无法处理的装饰器中。为 pykube.objects
添加类型存根将解决该问题。创建目录 _typeshed/pykube
并为 pykube
添加最小类型存根:
_typeshed/pykube/__init__.pyi
:
from typing import Any
def __getattr__(name: str) -> Any: ... # incomplete
_typeshed/pykube/objects.pyi
:
from typing import Any, ClassVar, Optional
from pykube.query import Query
def __getattr__(name: str) -> Any: ... # incomplete
class ObjectManager:
def __getattr__(self, name: str) -> Any: ... # incomplete
def __call__(self, api: Any, namespace: Optional[Any] = None) -> Query: ...
class APIObject:
objects: ClassVar[ObjectManager]
def __getattr__(self, name: str) -> Any: ... # incomplete
class NamespacedAPIObject(APIObject): ...
现在运行
$ MYPYPATH=_typeshed mypy pytest_helm_charts/
正确解析 obj_type.objects
:
T = TypeVar('T', bound=NamespacedAPIObject)
def wait_for_namespaced_objects_condition(obj_type: Type[T]) -> List[T]:
reveal_type(obj_type.objects)
输出:
pytest_helm_charts/utils.py:29: note: Revealed type is 'pykube.objects.ObjectManager'
我有一个具有以下签名的函数:
def wait_for_namespaced_objects_condition(
obj_type: Type[NamespacedAPIObject],
obj_condition_fun: Callable[[NamespacedAPIObject], bool],
) -> List[NamespacedAPIObject]:
...
此处重要的部分是 NamespacedAPIObject
参数。此函数将 obj_type
作为类型规范,然后创建该类型 (class) 的对象(实例)。然后将该类型的一些其他对象添加到列表中,然后使用 obj_condition_fun
过滤并作为类型 List[NamespacedAPIObject]. This works fine and also evaluates OK with
mypy`.
现在,我想让这个函数成为通用函数,这样就可以在 NamespacedAPIObject
的地方使用它的任何子类型。我的尝试是这样做的:
T = TypeVar("T", bound=NamespacedAPIObject)
def wait_for_namespaced_objects_condition(
obj_type: Type[T],
obj_condition_fun: Callable[[T], bool],
) -> List[T]:
但是 Type[T]
是 TypeVar
,所以这不是正确的方法。问题是:obj_type
参数的类型应该是什么才能使它起作用?我试过Generic[T]
,这对我来说似乎是最合理的,但它不起作用。如果我把 obj_type: T
放在那里,mypy
会评估这个 OK,但对我来说似乎是错误的。在我看来,这意味着 obj_type
是 class 的一个实例,它是 NamespacedAPIObject
的子类型,而我想说的是“T 是一个 class 变量表示 NamespacedAPIObject
的子类型。如何解决这个问题?
[编辑]
更好地解释一下(见下面的评论)为什么 Type[T]
对我不起作用:在我的例子中,T 的所有子类型都实现 class 方法 objects()
,但是当我尝试写我的代码是这样的:
obj_type.objects()
mypy
returns:
pytest_helm_charts/utils.py:36: error: "Type[T]" has no attribute "objects"
为什么 Type[T]
不是正确的选择?在我看来,它与 examples:
However using Type[] and a type variable with an upper bound we can do much better:
U = TypeVar('U', bound=User)
def new_user(user_class: Type[U]) -> U:
...
Now when we call new_user() with a specific subclass of User a type checker will infer the correct type of the result:
joe = new_user(BasicUser) # Inferred type is BasicUser
如果使用classmethod
:
from typing import Callable, Type, List, TypeVar
T = TypeVar('T', bound=Base)
class Base:
@classmethod
def objects(cls: Type[T]) -> List[T]:
...
def run(self):
...
class Derived(Base):
def run(self):
...
def foo(d: Derived) -> bool:
return True
def wait_for_namespaced_objects_condition(
obj_type: Type[T],
obj_condition_fun: Callable[[T], bool],
) -> List[T]:
a = obj_type.objects()
return a
wait_for_namespaced_objects_condition(Derived, foo)
But Type[T] is TypeVar, so it's not the way to go.
不,您走在正确的轨道上 - TypeVar
绝对是正确的选择。这里的问题在于 pykube.objects.APIObject
class 被包装在 mypy
尚无法处理的装饰器中。为 pykube.objects
添加类型存根将解决该问题。创建目录 _typeshed/pykube
并为 pykube
添加最小类型存根:
_typeshed/pykube/__init__.pyi
:from typing import Any def __getattr__(name: str) -> Any: ... # incomplete
_typeshed/pykube/objects.pyi
:from typing import Any, ClassVar, Optional from pykube.query import Query def __getattr__(name: str) -> Any: ... # incomplete class ObjectManager: def __getattr__(self, name: str) -> Any: ... # incomplete def __call__(self, api: Any, namespace: Optional[Any] = None) -> Query: ... class APIObject: objects: ClassVar[ObjectManager] def __getattr__(self, name: str) -> Any: ... # incomplete class NamespacedAPIObject(APIObject): ...
现在运行
$ MYPYPATH=_typeshed mypy pytest_helm_charts/
正确解析 obj_type.objects
:
T = TypeVar('T', bound=NamespacedAPIObject)
def wait_for_namespaced_objects_condition(obj_type: Type[T]) -> List[T]:
reveal_type(obj_type.objects)
输出:
pytest_helm_charts/utils.py:29: note: Revealed type is 'pykube.objects.ObjectManager'