23

我不确定类型提示实例变量的 Python 约定 - 我一直在__init__构造函数参数中执行它们,如下所示

class LoggedVar(Generic[T]):
    def __init__(self, value: T, name: str, logger: Logger) -> None:
        self.name = name
        self.logger = logger
        self.value = value`

但我也看到了注释实例变量的 PEP 约定(下面的片段),然后还在__init__参数中进行类型提示:

class BasicStarship:
    captain: str = 'Picard'               # instance variable with default
    damage: int                           # instance variable without default
    stats: ClassVar[Dict[str, int]] = {}  # class variable`

    def __init__(self, damage: int, captain: str = None):
        self.damage = damage
        if captain:
            self.captain = captain  # Else keep the default

最后,稍后在 PEP 526 文章中,他们说为了方便和惯例,可以执行以下操作:

class Box(Generic[T]):
    def __init__(self, content):
        self.content: T = content

(以上两个代码片段都来自这里。)

那么 - 这些约定之一是否比我应该尝试坚持的其他约定更好/更广泛接受(更好的可读性等)?

4

3 回答 3

13

我建议使用第一个版本,在__init__大多数情况下,您可以将类型分配给方法的参数。

该特定方法具有最少的冗余,同时仍允许类型检查器验证您__init__是否在代码的其他地方正确调用了该方法。

我建议使用第二个或第三个版本,当您的方法变得足够复杂到适用以下一项或多项的程度时,您可以在其中明确注释您的字段(内部或外部__init__) :__init__

  1. 您的字段到底从什么开始不再那么简单了
  2. 您的参数和字段之间不再存在一对一的映射
  3. 您有复杂的初始化逻辑,这会掩盖您的字段是如何分配的。

但是,我不清楚是首选第二个版本还是第三个版本——我个人更喜欢第三个版本,因为它在概念上更清晰,并且似乎没有混合实例与类属性的概念,但我不能否认第二个版本看起来更干净。

我在 'typing' gitter 频道上询问了这个问题,并得到了 Guido 的以下回复(如果你不知道,他制作了 Python,目前正在研究 mypy 和打字相关的东西):

无论哪种方式,似乎都有强烈的意见。我确实更喜欢将属性注释放在类主体中,而不是将它们洒在整个类__init__和其他方法中。我还认为,对于 PEP 526,这将是未来(还有基于类的 NamedTuple 声明,可能还有https://github.com/ericvsmith/dataclasses)。

引用链接

因此,似乎第二个版本比第三个更推荐,并且以这种方式定义类将在未来某个时候更深入地集成到 Python 语言本身!

编辑: PEP 557,数据类最近被接受并且似乎在轨道上(?)将包含在 Python 3.7 中。

于 2017-07-07T04:40:13.277 回答
2

@阿萨拉

似乎从 Python 3.8.10 / Mypy 0.910(2021 年 9 月)开始,在区分类定义中实例变量的类型注释和类定义中类(静态)变量的声明时,分配默认值会使一切变得不同。如果您不指定默认值(例如,x: int),Python 会将表达式视为类型注释;如果您指定一个默认值(例如,x: int = 42),Python 会将表达式视为类(静态)变量声明。

ClassVar可以使用语法为类定义中的类(静态)变量创建类型注释。如果您不指定默认值(例如, ),则不会y: ClassVar[int]创建事实类(静态)变量;如果您指定一个默认值(例如, ),创建一个事实类(静态)变量。y: ClassVar[int] = 69

于 2021-09-09T09:08:13.020 回答
2

我会坚持你在 中所做的事情LoggedVar,它遵循与 Python 中其他任何地方相同的规则,所以周围的混乱更少。

该类BasicStarShip通过将变量移出__init__函数来更改变量的范围。船长在那里宣布BasicStarShip.captain,将返回'Picard'

PEP 526 注释很好读,但它是针对特定情况的新规则,即__init__函数。来自Python 之禅

“特殊情况还不足以打破规则。”

于 2017-07-06T23:51:57.060 回答