我想允许使用 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)
应该有效?
当您指定
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
来注释一个类 argument 声明该参数是C
的实例(或C
的子类),但使用C
作为参数注释声明 参数是派生自Type[C]
(或C
本身)的类对象。C
如果我们查看
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)
希望这会有所帮助。
Type[A] 还接受类本身,但这并不总是需要的。
如果你希望你的函数只接受子类,你应该使用NewType,比如
class A:
pass
B = NewType('B', A)
def foo(cls: Type[B]):
...
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 子类属性的类型提示吗?