在我看来,我在注解类和示例变量的上下文中误解了PEP 526。
根据PEP文档中的示例,对象bar
应该是一个 * 示例变量 *,默认值为7
:
class Foo:
bar: int = 7
def __init__(self, bar):
self.bar = bar
但是用Python来测试它对我来说似乎是不同的。bar
是一个类和示例变量。
Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... bar: int = 7
... def __init__(self, bar):
... self.bar = bar
...
>>> Foo.bar
7
>>> f = Foo(3)
>>> f.bar
3
>>> f.__class__.bar
7
>>>
也许PEP不仅清楚这一点?恕我直言,PEP被违反了,目标没有达到。
1条答案
按热度按时间exdqitrt1#
没有冲突,PEP确实解释了正在发生的事情。
首先是:示例变量只是在示例上设置的名称(通常通过
self.[name] = ...
),类变量只是在类上设置的名称(通常通过在class ...
语句体中赋值给一个名称。它们不是互斥的。你可能忽略了Python如何在示例上查找名称。当你有一个示例
foo
,并且你想得到属性bar
时,foo.bar
实际上是由为类定义的代码来处理的,因为它需要为更高级的用例做一些检查(称为 * descriptor *),但对于常规变量,它将在返回到类变量之前从示例变量返回值。这是默认值的工作方式;如果示例上没有设置bar
,则使用类中的值。换句话说就是:在类上设置的示例变量的默认值仅在您 * 不 * 在示例上设置值时起作用:
你链接到的章节讨论了为什么需要为类型检查器*******类变量**做 * 注解。他们给予的例子有两个名称,一个 * 打算 * 作为示例属性的默认值,另一个 * 打算 * 只在类上设置。如果没有类型注解,类型检查器就不能区分这两种情况,因为它们看起来完全一样:
stats
dict是要在示例之间共享的,因此self.stats = ...
会遮蔽类变量并破坏预期用途:stats
是一个类变量(记录许多不同的每局统计数据),而captain
是一个示例变量,在类中设置了默认值。类型检查器可能看不到这种差异:这两个变量都在类中初始化,但是captain
只是作为示例变量的一个方便的默认值,而stats
是一个真正的类变量-它旨在由所有示例共享。使用
ClassVar[...]
可以让类型检查器知道创建示例属性应该是错误的,而赋值给self.captain
则不是:由于这两个变量都是在类级别初始化的,因此通过将类变量标记为使用
ClassVar[...]
中 Package 的类型进行注解来区分它们是很有用的。这样,类型检查器可以标记意外分配给示例上同名属性的值。请注意,PEP的目的是定义 type annotations 如何为变量工作,包括类和示例变量,* 而不是 * 定义Python变量在运行时如何工作!Type annotations被设计为静态分析工具的机器可读文档,并在那里帮助查找程序员错误。不要试图将其解读为Python runtime 如何工作的描述。