在我看来,在注释类和实例变量的上下文中,我误解了佩普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不仅仅是清楚的?IMHO - PEP遭到违反,目标没有实现。
发布于 2022-12-03 15:30:58
没有冲突,PEP确实解释了发生了什么。
首先:实例变量只是一个实例上的名称集(通常通过self.[name] = ...),类变量只是一个类上的名称集(通常是通过在class ...语句体中为名称赋值)。--它们不是相互排斥的。
您可能缺少的是Python如何在实例上查找名称。当您有一个实例foo并希望获得属性bar时,foo.bar实际上是由为该类定义的代码处理的,因为它需要对更高级的用例(称为描述符)进行一些检查,但是对于正则变量,它将在返回到类变量之前从实例变量返回值。--这是默认值如何工作的--;如果实例上没有设置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[...]包装的类型的注释来区分它们是非常有用的。通过这种方式,类型检查器可能会将意外分配标记为实例上同名的属性。
请注意,PEP的目的是定义类型注释如何为变量(包括类和实例变量)工作,而不是定义Python变量在运行时如何工作!类型注释被设计为用于静态分析工具的机器可读的文档,并且帮助查找程序员错误。不要试图将此理解为对Python运行时工作方式的描述。
https://stackoverflow.com/questions/74666784
复制相似问题