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

问题描述 投票:0回答:2

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

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"
python python-typing mypy
2个回答
4
投票

但是 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'

1
投票

为什么

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)
© www.soinside.com 2019 - 2024. All rights reserved.