我有一个具有以下签名的函数:
def wait_for_namespaced_objects_condition(
obj_type: Type[NamespacedAPIObject],
obj_condition_fun: Callable[[NamespacedAPIObject], bool],
) -> List[NamespacedAPIObject]:
...
这里重要的部分是
NamespacedAPIObject
参数。该函数采用 obj_type
作为类型规范,然后创建该类型(类)的对象(实例)。然后该类型的一些其他对象被添加到列表中,然后使用 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
评价这个没问题,但对我来说似乎是错误的。在我看来,这意味着 obj_type
是 NamespacedAPIObject
子类型的类的实例,而我想说的是“T 是代表 NamespacedAPIObject
子类型的类变量。如何解决这个问题?
[编辑] 为了更好地解释一下(请参阅下面的评论)为什么
Type[T]
对我不起作用:在我的例子中,T的所有子类型都实现类方法objects()
,但是当我尝试像这样编写代码时:
obj_type.objects()
mypy
返回:
pytest_helm_charts/utils.py:36: error: "Type[T]" has no attribute "objects"
但是 Type[T] 是 TypeVar,所以这不是正确的方法。
不,您走在正确的道路上 -
TypeVar
绝对是正确的选择。这里的问题在于 pykube.objects.APIObject
类被包装在 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'
为什么
Type[T]
不是正确的方法?在我看来,它与其中一个示例非常相似:
但是,使用 Type[] 和具有上限的类型变量,我们可以做得更好:
U = TypeVar('U', bound=User)
def new_user(user_class: Type[U]) -> U:
...
现在,当我们使用 User 的特定子类调用 new_user() 时,类型检查器将推断结果的正确类型:
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)