类型提示中的子类
- 2025-03-13 09:20:00
- admin 原创
- 18
问题描述:
我想允许使用 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 提示
Expected type A, got Type[B] instead.
我如何才能改变这里的类型提示来接受 A 的任何子类型?
根据PEP 484(“类型为特定参数类型的子类型的表达式也被该参数接受。”),我理解我的解决方案(cls: A)
应该有效?
解决方案 1:
当您指定时cls: A
,您说的是cls
需要一个类型的实例A
。
对于 python 3.5.2 到 3.8,指定cls
为类型A
(或其子类型)的类对象的类型提示使用typing.Type
。
from typing import Type
def process_any_subclass_type_of_A(cls: Type[A]):
pass
从类对象的类型来看:
有时您想讨论从给定类继承的类对象。这可以拼写为
Type[C]
其中C
是类。换句话说,当C
是类的名称时,使用C
注释参数声明该参数是C
(或子类的C
)实例,但使用Type[C]
作为参数注释声明该参数是从C
(或其C
自身)派生的类对象。
从 python 3.9 开始,建议使用内置函数type
。
def process_any_subclass_type_of_A(cls: type[A]):
pass
解决方案 2:
如果我们查看模块Type
的描述typing
,那么我们会看到这些文档:
可用于注释类对象的特殊构造。
例如,假设我们有以下类:
class User: ... # Abstract base for User classes class BasicUser(User): ... class ProUser(User): ... class TeamUser(User): ...
并且一个函数接受一个 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)
希望这会有所帮助。
解决方案 3:
Type[A] 也接受类本身,但这并不总是需要的。
如果你希望你的函数只接受子类,你应该使用NewType,例如
class A:
pass
B = NewType('B', A)
def foo(cls: Type[B]):
...
解决方案 4:
从 Python 3.9 开始,可以使用内置函数type
来类型提示类对象,即无需导入typing.Type
。
def process_any_subclass_type_of_A(cls: type[A]):
pass
process_any_subclass_type_of_A(B) # <--- passed the class B itself
另一方面,如果你想要对 的任何子类的实例A
进行类型提示,那么 OP 的代码应该可以解决问题。换句话说:
def process_any_subclass_type_of_A(obj: A):
pass
process_any_subclass_type_of_A(B()) # <--- passed an instance of B