在 __init__ 中更改时,python 类变量可以成为实例变量吗?

Asa*_*ara 7 python type-hinting

据我了解,这里var是一个类变量

class MyClass:
    var = 'hello'

    def __init__(self):
        print(self.var)
Run Code Online (Sandbox Code Playgroud)

那是一个实例变量

class MyClass:

    def __init__(self, var):
        self.var = var
        print(self.var)
Run Code Online (Sandbox Code Playgroud)

我遇到了问题,我正在寻找一种方法来使实例变量的类型提示成为可能。我当然可以使用类型提示参数,def __init__(self, var: str):但这不会影响实例变量本身。

然后我注意到在一些描述中(如这里),他们使用术语实例变量来表示var这样的:

class MyClass:
    var : str = 'hello'

    def __init__(self, var : str = None):
        self.var = var if var
        print(self.var)
Run Code Online (Sandbox Code Playgroud)

这确实是解决方案,但这仍然是一个实例变量吗?因为它是在类体中定义的,所以在我的理解中它将是一个类变量。如果您要为 var 使用列表,则对该列表变量的所有更改都将通过实例共享。

但在这种情况下不会有问题,因为字符串被替换并且不会被其他实例共享。但是,如果您将其称为实例变量,对我来说似乎是错误的,而我不知道是否应该像这样使用它只是为了让类型提示起作用。

Mic*_*x2a 9

这确实是解决方案,但这仍然是一个实例变量吗?因为它是在类体中定义的,所以在我的理解中它将是一个类变量。[...snip...] 但是,如果您将其称为实例变量,我认为这似乎是错误的,而我不知道是否应该像这样使用它只是为了让类型提示起作用。

对于它的价值,我也有同样的不适。看起来我们在概念上混合了两个概念只是为了有更清晰的类型注释。

但是,我已经问过 Guido 一两次了,他似乎确实更喜欢将这些类属性视为实例属性。

无论如何,要回答您的核心问题,如果我们这样做:

class Test:
    field1: int
    field2: str = 'foo'
Run Code Online (Sandbox Code Playgroud)

然后...

  1. 符合 PEP 484 和 526 的类型检查器将此类视为:
    1. 它有一个名为的实例属性 field1
    2. 它有一个名为的实例属性field2,其默认值为“foo”(根据PEP 526)。
  2. 在运行时,忽略类型提示,Python 将:
    1. 添加名为Test的类注释field1,但添加类属性。(类注释不会自动变成类属性。)
    2. 添加名为Test的类注释field2以及名为包含值“foo”的类属性field2

所以,它可能会有点混乱。

但无论如何,这引出了一个问题:我们如何向类型检查器表明我们希望某个字段真正成为类属性?

好吧,事实证明 PEP 484 半最近被修改为包含ClassVar类型注释,它正是这样做的。

所以,如果我们想添加一个新的类属性,我们可以这样做:

from typing import ClassVar

class Test:
    field1: int
    field2: str = 'foo'
    field3: ClassVar[int] = 3
Run Code Online (Sandbox Code Playgroud)

所以现在,field3应该被视为一个类属性,默认值为“3”。

(注意:ClassVar 是typing为 Python 3.5.3添加的——如果您使用的是typing与 Python 3.5 捆绑的旧版本,您可以typing_extensions通过 pip安装第三方模块并ClassVar从那里导入来获得该类型的“反向移植”反而。)

我认为您是否决定采用这种方法还是不使用它是个人偏好。

一方面,Guido 的观点,几乎根据定义,定义了“Pythonic”与否,所以从这个立场来看,采用这个新的习语是没有问题的。此外,语言本身正在缓慢但肯定地转向采用这种新的习惯用法——例如,参见最近接受的PEP 557,它最终遵循了将类属性/类注释视为实例属性的相同习惯用法。

另一方面,很难摆脱这种微妙差异会导致问题的唠叨。在这种情况下,您可以坚持使用仅将所有字段设置在__init__. 这种方法还具有使您的代码与 Python 2 和 3.x - 3.5 兼容的好处。

一个中间立场可能是简单地从不以任何方式、形状或形式使用类属性,而只是坚持使用类注释。这有点限制,因为我们不能再给我们的实例变量默认值,但我们现在可以避免将类属性与实例属性完全混淆。(如前所述,并在评论中指出,类注释不会作为类属性添加。)

  • 绝对同意混合的奇怪之处。 (3认同)