据我了解,
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):
输入参数,但这不会影响 实例变量 本身。
然后我注意到在一些描述中(比如这里)他们使用术语实例变量来表示
var
,如下所示:
class MyClass:
var : str = 'hello'
def __init__(self, var : str = None):
self.var = var if var
print(self.var)
这确实是解决方案,但这仍然是一个实例变量吗?因为它是在类主体中定义的,所以在我的理解中它将是一个类变量。如果您使用 var 列表,则对此列表 var 的所有更改都将在实例上共享。
但在这种情况下不会有问题,因为字符串被替换并且不会被其他实例共享。但是,如果您将其称为 实例变量 ,对我来说似乎是错误的,并且我不知道是否应该像这样使用它只是为了让类型提示起作用。
这确实是解决方案,但这仍然是一个实例变量吗?因为它是在类主体中定义的,所以在我的理解中它将是一个类变量。 [...snip...] 但是,如果您将其称为实例变量,那么对我来说似乎是错误的,并且我不知道是否应该像这样使用它只是为了让类型提示起作用。
不管怎样,我也有同样的不适。看起来我们在概念上混合了两个概念,只是为了拥有更清晰的类型注释。
但是,我已经问过 Guido 一两次这个问题,看来他确实更喜欢将这些类属性视为实例属性。
无论如何,要回答您的核心问题,如果我们这样做:
class Test:
field1: int
field2: str = 'foo'
然后...
field1
field2
的实例属性,默认值为“foo”(根据 PEP 526)。field1
的类 annotation,但不是类 attribute。 (类注释不会自动转换为类属性。)
field2
的类 annotation添加到 Test 以及包含值“foo”的名为
field2
的类 attribute。
所以,它可能会有点混乱。
但无论如何,这引出了一个问题:我们如何向类型检查器表明我们希望某个字段真正成为类属性?
ClassVar
类型注释,它正是这样做的。
所以,如果我们想添加一个新的类属性,我们可以这样做:
from typing import ClassVar
class Test:
field1: int
field2: str = 'foo'
field3: ClassVar[int] = 3
所以现在,
field3
应该被视为默认值为“3”的类属性。
(注意:ClassVar 已添加到 Python 3.5.3 的
typing
中——如果您使用的是与 Python 3.5 捆绑在一起的旧版本 typing
,您可以通过安装 typing_extensions
来获得该类型的“向后移植”
通过 pip 的第三部分模块并从那里导入 ClassVar
。)
我认为您是否决定接受这种方法或不使用它是个人喜好。
一方面,Guido 的观点,几乎从定义上来说,定义了什么是“Pythonic”或什么不是,所以从这个立场来看,采用这个新的习惯用法是没有问题的。此外,语言本身正在缓慢但肯定地转向采用这种新的习惯用法——例如,参见最近接受的PEP 557,它最终遵循将类属性/类注释视为实例属性的相同习惯用法。
另一方面,很难摆脱这种挥之不去的担忧,即这种微妙的差异会导致未来的问题。在这种情况下,您可以坚持使用标准方法,将所有字段设置在
__init__
内。这种方法还具有保持代码与 Python 2 和 3.x - 3.5 兼容的好处。
中间立场可能是根本不以任何方式、形状或形式使用类属性,而只坚持使用类注释。这有点限制,因为我们不能再给实例变量提供默认值,但我们现在可以完全避免将类属性与实例属性混为一谈。 (如前所述,以及评论中指出的,类annotations不会添加为类attributes。)