繁体   English   中英

Python 3.6 类型提示,用于 function 接受泛型 class 类型和相同泛型类型的实例类型

[英]Python 3.6 type hinting for a function accepting generic class type and instance type of the same generic type

我有一个带有以下签名的 function:

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

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

现在,我想让这个 function 通用,以便可以使用它的任何子类型来代替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 ,所以它不是 go 的方式。 问题是: obj_type参数的类型应该是什么才能使这项工作? 我尝试了Generic[T] ,这对我来说似乎是最合理的,但它不起作用。 如果我把obj_type: T放在那里, mypy评估结果还可以,但对我来说似乎是错误的。 在我看来,这意味着obj_type是 class 的一个实例,它是NamespacedAPIObject的子类型,而我想说的是“T 是代表NamespacedAPIObject子类型的 class 变量。如何解决这个问题?

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

obj_type.objects()

mypy返回:

pytest_helm_charts/utils.py:36: error: "Type[T]" has no attribute "objects"

但是 Type[T] 是 TypeVar,所以它不是 go 的方式。

不,你在正确的轨道上 - TypeVar绝对是通往 go 的方式。 这里的问题在于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)

Output:

pytest_helm_charts/utils.py:29: note: Revealed type is 'pykube.objects.ObjectManager'

为什么Type[T]不是 go 的方式? 在我看来,它与其中一个示例非常相似:

然而,使用 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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM