类型提示中的子类

Subclass in type hinting

我想允许类型提示使用 Python 3 接受某个 class 的子 classes。例如:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

def process_any_subclass_type_of_A(cls: A):
    if cls == B:
        # do something
    elif cls == C:
        # do something else

现在输入以下代码时:

process_any_subclass_type_of_A(B)

我收到 PyCharm IDE 提示 'Expected type A, got Type[B] instead.'

如何在此处更改类型提示以接受 A 的任何子类型?

根据 PEP 484(“其类型是特定参数类型的子类型的表达式也被该参数接受。”),我明白我的解决方案 (cls: A) 应该有效吗?

当您指定 cls: A 时,您是在说 cls 需要类型 A 实例 。将 cls 指定为类型 A(或其子类型)的 class 对象的类型提示使用 typing.Type.

from typing import Type
def process_any_subclass_type_of_A(cls: Type[A]):
    pass

来自class个对象的类型 :

Sometimes you want to talk about class objects that inherit from a given class. This can be spelled as Type[C] where C is a class. In other words, when C is the name of a class, using C to annotate an argument declares that the argument is an instance of C (or of a subclass of C), but using Type[C] as an argument annotation declares that the argument is a class object deriving from C (or C itself).

如果我们查看 typing 模块中的 Type 描述,那么我们会看到这些文档:

A special construct usable to annotate class objects.

For example, suppose we have the following classes::

 class User: ...  # Abstract base for User classes
 class BasicUser(User): ...
 class ProUser(User): ...
 class TeamUser(User): ...

And a function that takes a class argument that's a subclass of User and returns an instance of the corresponding class::

 U = TypeVar('U', bound=User)
 def new_user(user_class: Type[U]) -> U:
     user = user_class()
     # (Here we could write the user object to a database)
     return user

 joe = new_user(BasicUser)

At this point the type checker knows that joe has type BasicUser.

基于此,我可以想象一个合成示例,重现 PyCharm 中类型提示错误的问题。

from typing import Type, Tuple

class BaseClass: ...

class SubClass(BaseClass): ...
class SubSubClass(SubClass): ...

def process(model_instance: BaseClass, model_class: Type[BaseClass]) -> Tuple[BaseClass, BaseClass]:
    """ Accepts all of the above classes """
    return model_instance, model_class()


class ProcessorA:
    @staticmethod
    def proc() -> Tuple[SubClass, SubClass]:
        """ PyCharm will show an error 
        `Expected type 'tuple[SubClass, SubClass]', got 'tuple[BaseClass, BaseClass]' instead` """
        return process(SubClass(), SubClass)


class ProcessorB:
    @staticmethod
    def proc() -> Tuple[SubSubClass, SubSubClass]:
        """ PyCharm will show an error 
        `Expected type 'tuple[SubSubClass, SubSubClass]', got 'tuple[BaseClass, BaseClass]' instead` """
        return process(SubSubClass(), SubSubClass)

但我们在 Type 的文档中看到,可以通过使用 TypeVarbound 参数来纠正这种情况。然后在BaseClass声明为类型的地方使用它。

from typing import TypeVar, Type, Tuple

class BaseClass: ...

B = TypeVar('B', bound=BaseClass)

class SubClass(BaseClass): ...
class SubSubClass(SubClass): ...

def process(model_instance: B, model_class: Type[B]) -> Tuple[B, B]:
    """ Accepts all of the above classes """
    return model_instance, model_class()


class ProcessorA:
    @staticmethod
    def proc() -> Tuple[SubClass, SubClass]:
        return process(SubClass(), SubClass)


class ProcessorB:
    @staticmethod
    def proc() -> Tuple[SubSubClass, SubSubClass]:
        return process(SubSubClass(), SubSubClass)

希望这会有所帮助。