python-3.x 尝试理解PEP 526中的“类和示例变量注解”

9rygscc1  于 2022-12-05  发布在  Python
关注(0)|答案(1)|浏览(105)

在我看来,我在注解类和示例变量的上下文中误解了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被违反了,目标没有达到。

exdqitrt

exdqitrt1#

没有冲突,PEP确实解释了正在发生的事情。
首先是:示例变量只是在示例上设置的名称(通常通过self.[name] = ...),类变量只是在类上设置的名称(通常通过在class ...语句体中赋值给一个名称。它们不是互斥的
你可能忽略了Python如何在示例上查找名称。当你有一个示例foo,并且你想得到属性bar时,foo.bar实际上是由为类定义的代码来处理的,因为它需要为更高级的用例做一些检查(称为 * descriptor *),但对于常规变量,它将在返回到类变量之前从示例变量返回值。这是默认值的工作方式;如果示例上没有设置bar,则使用类中的值。
换句话说就是:在类上设置的示例变量的默认值仅在您 * 不 * 在示例上设置值时起作用:

>>> class Foo:
...     bar: int = 7
...     def __init__(self, bar: int | None):
...         if bar is not None:
...             self.bar = bar
...
>>> f1 = Foo()  # default value for bar
>>> f2 = Foo(3)  # explicit value set
>>> Foo.bar  # default on class
7
>>> f1.bar   # is returned here
7
>>> "bar" in vars(f1)  # as there is no instance variable
False
>>> f2.bar  # gets the instance value
3
>>> vars(f2)["bar"]  # from the instance namespace
3

你链接到的章节讨论了为什么需要为类型检查器*******类变量**做 * 注解。他们给予的例子有两个名称,一个 * 打算 * 作为示例属性的默认值,另一个 * 打算 * 只在类上设置。如果没有类型注解,类型检查器就不能区分这两种情况,因为它们看起来完全一样:

class Starship:
    captain = 'Picard'
    stats = {}

stats dict是要在示例之间共享的,因此self.stats = ...会遮蔽类变量并破坏预期用途:
stats是一个类变量(记录许多不同的每局统计数据),而captain是一个示例变量,在类中设置了默认值。类型检查器可能看不到这种差异:这两个变量都在类中初始化,但是captain只是作为示例变量的一个方便的默认值,而stats是一个真正的类变量-它旨在由所有示例共享。
使用ClassVar[...]可以让类型检查器知道创建示例属性应该是错误的,而赋值给self.captain则不是:
由于这两个变量都是在类级别初始化的,因此通过将类变量标记为使用ClassVar[...]中 Package 的类型进行注解来区分它们是很有用的。这样,类型检查器可以标记意外分配给示例上同名属性的值。
请注意,PEP的目的是定义 type annotations 如何为变量工作,包括类和示例变量,* 而不是 * 定义Python变量在运行时如何工作!Type annotations被设计为静态分析工具的机器可读文档,并在那里帮助查找程序员错误。不要试图将其解读为Python runtime 如何工作的描述。

相关问题