类型提示中的子类

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

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

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 提示“预期类型 A,而是类型 [B]。”

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

根据PEP 484(“该参数也接受其类型是特定参数类型的子类型的表达式。”),我明白我的解决方案

(cls: A)
应该有效?

python subclass type-hinting python-typing
4个回答
310
投票

当您指定

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

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

来自 类对象的类型

有时你想讨论继承自某个类的类对象 给定的类。这可以拼写为

Type[C]
,其中
C
是一个类。在 换句话说,当
C
是类名时,使用
C
来注释一个类 argument 声明该参数是
C
的实例(或
C
的子类),但使用
Type[C]
作为参数注释声明 参数是派生自
C
(或
C
本身)的类对象。


26
投票

如果我们查看

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

可用于注释类对象的特殊构造。

例如,假设我们有以下类::

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

还有一个函数,它接受一个类参数,该类参数是 用户并返回相应类的实例::

 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)

此时类型检查器知道 joe 的类型为 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
的文档中看到,可以通过将
TypeVar
bound
参数一起使用来纠正这种情况。然后在
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)

希望这会有所帮助。


7
投票

Type[A] 还接受类本身,但这并不总是需要的。

如果你希望你的函数只接受子类,你应该使用NewType,比如

class A: pass B = NewType('B', A) def foo(cls: Type[B]): ...
    

0
投票
我理解回复,但看看这个案例:

class Animal: def __init__(self) -> None: self.ismammal = True class Dog(Animal): def __init__(self) -> None: super().__init__() self.sound = 'woof' def get_animal() -> Animal: return Dog() get_animal(). # VS code only suggests *.ismammal but not *.sound
我怎样才能做到 get_animal().会显示带有所有 Animal 子类属性的类型提示吗?

© www.soinside.com 2019 - 2024. All rights reserved.