在__init __中更改python类变量可以成为实例变量吗?

时间:2017-11-28 13:12:34

标签: python type-hinting

据我所知,var是一个类变量

class MyClass:
    var = 'hello'

    def __init__(self):
        print(self.var)

那就是一个实例变量

class MyClass:

    def __init__(self, var):
        self.var = var
        print(self.var)

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

然后我在一些描述(例如here)中注意到他们使用实例变量这个术语来代替var

class MyClass:
    var : str = 'hello'

    def __init__(self, var : str = None):
        self.var = var if var
        print(self.var)

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

但是在这种情况下没有问题,因为字符串被替换而不会被其他实例共享。但是,如果你把它称为实例变量,我似乎不对,我不知道我是否应该这样使用它只是为了让类型提示工作。

1 个答案:

答案 0 :(得分:3)

  

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

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

然而,我已经问过Guido一两次这个问题,看起来他确实更喜欢将这些类属性视为实例属性。

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

class Test:
    field1: int
    field2: str = 'foo'

则...

  1. PEP 484和526兼容类型检查器会将此类视为:
    1. 它有一个名为field1
    2. 的实例属性
    3. 它有一个名为field2的实例属性,默认值为'foo'(根据PEP 526)。
  2. 在运行时,忽略类型提示,Python将:
    1. 将一个名为field1的类注释添加到Test,但一个类属性。 (类注释不会自动转换为类属性。)
    2. 将名为field2的类注释添加到Test以及名为field2的类属性,其中包含值'foo'。
  3. 所以,它可能会有点混乱。

    但不管怎么说,这就引出了一个问题:我们如何向类型检查器指出我们希望某个字段真正成为类属性?

    嗯,事实证明PEP 484最近被半修改以包含ClassVar type annotation,这正是如此。

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

    from typing import ClassVar
    
    class Test:
        field1: int
        field2: str = 'foo'
        field3: ClassVar[int] = 3
    

    现在,field3应被视为类属性,默认值为“3”。

    (注意:对于Python 3.5.3,ClassVar已添加到typing - 如果您使用的是与Python 3.5捆绑在一起的旧版typing,则可以获得通过pip安装typing_extensions第三方模块并从那里导入ClassVar来输入。)

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

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

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

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