实例变量的类型提示约定 Python

Type Hints Convention for Instance Variables Python

我不确定 Python 类型提示实例变量的约定 - 我一直在 __init__ 构造函数参数中使用它们 like seen here:

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

(以上两段代码均来自here。)

那么 — 这些约定中的一个 better/more 是否比我应该尝试坚持的其他约定更广为接受(更好的可读性等)?

我会坚持你在 LoggedVar 中所做的事情,它遵循与 Python 中其他地方相同的规则,因此不会出现混乱。

BasicStarShip class 通过将变量移出 __init__ 函数来更改变量的范围。随着船长在那里宣布,BasicStarShip.captain,将 return 'Picard'.

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

"Special cases aren't special enough to break the rules."

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

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

我建议使用第二个或第三个版本,当您的 __init__ 方法变得足够复杂到一个或以下更多适用:

  1. 字段的开头不再那么简单
  2. 您的参数和字段之间不再存在一对一的映射
  3. 您有复杂的初始化逻辑,混淆了您的字段是如何分配的。

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

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

There seem to be strong opinions either way. I do indeed prefer putting attribute annotations in the class body rather than sprinkling them throughout __init__ and other methods. I also think that with PEP 526 this will be the future (also with things like class-based NamedTuple declarations and possibly https://github.com/ericvsmith/dataclasses).

(link to quote)

因此,似乎第二个版本优于第三个版本,以这种方式定义 classes 将在未来的某个时候更深入地融入 Python 语言本身未来!

编辑: PEP 557, data classes was recently accepted 似乎在轨道上 (?) 将包含在 Python 3.7.

@阿萨拉

似乎从 Python 3.8.10 / Mypy 0.910(2021 年 9 月)开始,在区分 class 定义和声明中实例变量的类型注释时对于 class 定义中的 class(静态)变量,默认值的赋值会产生重大影响。如果您没有指定默认值(例如,x: int),Python 会将表达式视为类型注释;如果您分配默认值(例如 x: int = 42),Python 会将表达式视为 class(静态)变量声明。

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