类成员的类型推断

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

使用

TypeVar
Generic
,我可以使用可以推断类型的方法创建类,例如:

from typing import TypeVar, Generic T = TypeVar('T') class Box(Generic[T]): def __init__(self, content: T) -> None: self.content = content Box(1) # OK, inferred type is Box[int]
是否可以推断类成员的类型?假设我有不同类型的运动员统计数据:

from abc import ABC from typing import Generic, TypeVar, Type class Stats(ABC): pass class BaseballStats(Stats): @property def batting_average(self) -> float: return 0.314 class FootballStats(Stats): @property def yards(self) -> int: return 314
现在我想为运动员定义一个抽象类:

S = TypeVar('S', bound=Stats) class Athlete(ABC, Generic[S]): _stats_type: Type[S] @property def stats(self) -> S: return self._stats_type()
如果我尝试创建一个 

BaseballPlayer

 并让它通过分配给 
_stats_type
 来推断类型,它不会:

class BaseballPlayer(Athlete): _stats_type = BaseballStats bo = BaseballPlayer() # reveal_type(bo.stats) --> gives `Any`; type was not inferred print(bo.stats.batting_average) # works.
如果我指定 

Athlete

 的类型但不分配 
_stats_type
,它将不会运行。

class FootballPlayer(Athlete[FootballStats]): pass bo = FootballPlayer() # reveal_type(bo.stats) --> gives `FootballStats` because I explcitly called it out print(bo.stats.yards) # AttributeError: 'FootballPlayer' object has no attribute '_stats_type'
有没有一种替代方案不需要我将统计类型放在两个地方?即,我可以避免这样做吗?

class FootballPlayer(Athlete[FootballStats]): _stats_type = FootballStats
    
python mypy python-typing
1个回答
0
投票
首先,在定义子类时,你几乎必须指定

Athlete

 的类型,否则 mypy 会抱怨以下内容:

error: Missing type parameters for generic type "Athlete" [type-arg]
现在,与您的 

Box

 示例相反,您的问题是您希望在运行时拥有类型 
(以便您可以在 stats
 中实例化它)。

我的经验(也被其他人分享,请参见例如

beartype)是,在运行时使用类型提示至少可以说有点笨拙。

话虽如此,您可能可以执行如下操作,在运行时从您的类继承的

Athlete

 获取类型。它基于来自 
PEP 560
__orig_bases__

from typing import cast, get_args, get_origin class Athlete(ABC, Generic[S]): @property def _stats_type(self) -> Type[S]: for orig_bases in self.__orig_bases__: if get_origin(orig_bases) == Athlete: return cast(Type[S], get_args(orig_bases)[0]) raise RuntimeError # should not happen @property def stats(self) -> S: return self._stats_type() class FootballPlayer(Athlete[FootballStats]): pass bo = FootballPlayer() #reveal_type(bo.stats) --> gives `FootballStats` print(bo.stats.yards)
但请注意 PEP 发出的关于 

__orig_bases__

 的以下警告:

注意:这两个方法名称保留供类型模块和通用类型机器使用,不鼓励任何其他使用。

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