【发布时间】:2022-01-10 06:34:10
【问题描述】:
我刚刚进入 Python 中更复杂的类型提示内容,特别是 typing.Generic。
假设我有一个基类和一个子类:
class Base:
def base_method(self):
pass
class Sub(Base):
def sub_method(self):
pass
现在我想创建一个类,它的实例变量可以是“Base 或其任何子类”。为此,我必须使用typing.TypeVar 和typing.Generic:
class FullyTypedContainer(Generic[BaseOrSubclass]):
def __init__(self, p: BaseOrSubclass):
self._p = p
@property
def p(self) -> BaseOrSubclass:
return self._p
这很好用; p 的类型是通过 BaseOrSubclass “传递”的,因此像 PyLance 这样的语言服务器会看到 FullTypedContainer(Sub()).p 有一个名为 sub_method() 的方法,但 FullyTypedContainer(Base()).p 没有。
那么这个函数的类型提示是什么?
def get_random_list_of_containers():
a = random.randint(0, 1)
if a == 0:
return [FullyTypedContainer(Sub()), FullyTypedContainer(Base())]
else:
return [FullyTypedContainer(Base()), FullyTypedContainer(Sub())]
-
-> typing.List[FullyTypedContainer]:没有指定FullyTypedContainer的类型,因此get_list_of_containers()[0].p似乎被视为Any类型。 -
-> typing.List[FullyTypedContainer[Base]]:“强制”所有类型为Base,(不是Base或其子类之一),因此get_list_of_containers()[0].p被视为从未拥有sub_method()方法。 -
-> typing.List[FullyTypedContainer[typing.Union[Base, Sub]]:似乎是最好的选择,但需要我手动维护Base的每个子类的列表。
【问题讨论】:
-
你真的需要列表吗?这应该是
tuple吗? -
@juanpa.arrivillaga
tuple或list无关紧要;关键是我想知道如何输入从没有参数的函数返回的Generics。 -
嗯,这是相关的,因为元组可以包含并且可以异构类型,但列表必须是同质的
-
@juanpa.arrivillaga 我现在看到了,但这实际上与我的问题相反,所以我正在编辑。
-
我不太清楚您的期望究竟是什么。由于输出完全是随机的,因此类型检查器必须始终假设
Base的最坏情况。即使它是“Base或子类”,这仍然意味着不能保证比Base更多的功能。类型检查器无法知道get_list_of_containers()[0].p绝对不仅仅是Base。那么,你能澄清一下类型检查器在实践中应该从注解中推断出什么吗?
标签: python python-3.x type-hinting mypy typing