Python 3.6 类型提示接受同一泛型类型的泛型类类型和实例类型的函数

pio*_*tec 6 python type-hinting python-3.x mypy python-typing

我有一个具有以下签名的函数:

def wait_for_namespaced_objects_condition(
    obj_type: Type[NamespacedAPIObject],
    obj_condition_fun: Callable[[NamespacedAPIObject], bool],
) -> List[NamespacedAPIObject]:
...
Run Code Online (Sandbox Code Playgroud)

这里的重要部分是NamespacedAPIObject参数。此函数采用obj_typeas 类型规范,然后创建该类型(类)的对象(实例)。然后将该类型的其他一些对象添加到列表中,然后使用mypy`obj_condition_fun类型List[NamespacedAPIObject]. This works fine and also evaluates OK with 对其进行过滤并返回。

现在,我想让这个函数通用,以便NamespacedAPIObject可以使用它的任何子类型。我的尝试是这样做的:

T = TypeVar("T", bound=NamespacedAPIObject)

def wait_for_namespaced_objects_condition(
    obj_type: Type[T],
    obj_condition_fun: Callable[[T], bool],
) -> List[T]:

Run Code Online (Sandbox Code Playgroud)

但是Type[T]TypeVar,所以这不是要走的路。问题是:应该使用什么类型的obj_type参数才能使这项工作起作用?我试过Generic[T],这对我来说似乎是最合理的,但它不起作用。如果我把它放在那里obj_type: Tmypy评估这可以,但对我来说似乎是错误的。在我看来,这意味着它obj_type是一个子类型的类的实例NamespacedAPIObject,而我想说的是“T 是一个表示 子类型的类变量NamespacedAPIObject。如何解决这个问题?

[编辑] 为了更好地解释(请参阅下面的评论)为什么Type[T]对我不起作用:在我的情况下, T 的所有子类型都实现了 class method objects(),但是当我尝试像这样编写代码时:

obj_type.objects()
Run Code Online (Sandbox Code Playgroud)

mypy 返回:

pytest_helm_charts/utils.py:36: error: "Type[T]" has no attribute "objects"
Run Code Online (Sandbox Code Playgroud)

hoe*_*ing 4

但 Type[T] 是 TypeVar,所以这不是正确的方法。

不,你走在正确的道路上——这TypeVar绝对是正确的道路。这里的问题在于类被包装在尚无法处理的pykube.objects.APIObject装饰器中。mypy添加类型存根 forpykube.objects将解决该问题。创建一个目录_typeshed/pykube并添加最小类型存根pykube

  • _typeshed/pykube/__init__.pyi:

    from typing import Any
    
    def __getattr__(name: str) -> Any: ...  # incomplete
    
    Run Code Online (Sandbox Code Playgroud)
  • _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): ...
    
    Run Code Online (Sandbox Code Playgroud)

正在运行

$ MYPYPATH=_typeshed mypy pytest_helm_charts/
Run Code Online (Sandbox Code Playgroud)

正确解析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)
Run Code Online (Sandbox Code Playgroud)

输出:

pytest_helm_charts/utils.py:29: note: Revealed type is 'pykube.objects.ObjectManager'
Run Code Online (Sandbox Code Playgroud)