在python类中声明的实例变量位于具有类型暗示的方法之外

2024-09-26 22:08:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在努力学习Python变量注释。根据PEP-0526,我们现在有了类似于:

class BasicStarship:
    captain: str = 'Picard'               # instance variable with default
    damage: int                           # instance variable without default
    stats: ClassVar[Dict[str, int]] = {}  # class variable 

这是一个艰难的周末,我的Python有点生疏,但我认为未分配给self的声明变量是类变量。以下是一些有趣的例子:

class BasicStarship:
        captain: str = 'Picard'               # instance variable with default

        def __init__(self,name):
            self.captain = name

wtf = BasicStarship('Jim')
BasicStarship.captain = 'nope'

print(wtf.captain) #=> Jim

上面的代码正如我所期望的那样工作。不过下面有点让我困惑

class BasicStarship:
        captain: str = 'Picard'               # instance variable with default

wtf = BasicStarship()
BasicStarship.captain = 'nope'

print(wtf.captain) #=> 'nope'

在第二个例子中,我希望是“Picard”而不是“nope”。我觉得我缺少了一些关于类变量和实例变量的规则。在某种程度上,我认为执行BasicStarship.captain会导致类错误,因为captain是一个实例变量(在第一个示例中,不确定在第二个示例中)。您是否总是能够在类声明之后定义实例变量(在方法之外)?类和实例变量之间是否存在某种相互作用,从而使这一点更清楚

使用Python 3.6.3运行的代码


Tags: 实例instanceselfdefault声明withvariableclass
2条回答

这是类变量和实例变量的正常相互作用。它与打字无关

class Quux:
    foo = 1
    bar = 2

    def __init__(self):
        self.bar = 3
        self.baz = 4

quux = Quux()
Quux.foo    # => 1   - class variable
Quux.bar    # => 2   - class variable
quux.foo    # => 1   - class variable, because no instance variable
quux.bar    # => 3   - instance variable shadowing the class variable
quux.baz    # => 4   - instance variable

您的错误是这里的措辞:

captain: str = 'Picard'               # instance variable with default

captain不是此处的实例变量。这定义了一个类变量,当未设置相应的实例变量时,该类变量作为默认值。但是类变量和实例变量是两个独立的东西

请注意,类型仅由静态类型检查器进行计算,而不是由Python解释器本身进行计算。因此,这两者之间不存在任何运行时语义差异:

class CheckedQuux:
    foo: Dict[str, int] = {}
    bar: ClassVar[Dict[str, int]] = {}

在运行时,它们都是空字典对类变量的赋值。如果注释使其中一个定义了实例变量,而另一个定义了类变量,则会违反键入不能具有运行时效果的规则。ClassVar下的注释具有误导性,尽管文本给出了关于其含义的提示:注释用于指示变量将如何使用的意图。类型检查器假设上面的foo将从实例(checked_quux.foo)访问,并且可能会被实例变量覆盖,而bar不应在实例上访问,而应仅在类(CheckedQuux.bar)上访问,如果我们试图在实例(checked_quux.bar)上访问它,则应引发类型检查错误

我同意您对文档的一些困惑,因为您的示例中的captain似乎是类属性而不是实例属性

考虑如下:

class BasicStarship:
    captain = 'Picard'

    def __init__(self, captain=None):
        if captain:
            self.captain = captain


wtf1 = BasicStarship()
wtf2 = BasicStarship('Me!')

BasicStarship.captain = 'Riker'

print(wtf1.captain)
print(wtf2.captain)

如您所料(根据您的问题),此打印:

Riker
Me!

然而,这很有趣:

print(wtf1.__dict__)
print(wtf2.__dict__)
print(BasicStarship.__dict__)

结果:

{}
{'captain': 'Me!'}
{'__module__': '__main__', 'captain': 'Riker', '__init__': <etc.> }

因此,wtf1没有一个名为captain的属性,因此使用名为captain的类属性(解释了当您更改它时为什么会更改)wtf2确实有一个名为captain的属性,因此它会覆盖class属性BasicStarship显示class属性

这一切都是有意义的,并且与实际文档中给出的示例类似,唯一令人困惑的部分是描述为instance variable with default,因为它似乎更正确,为class variable with default

(在Python 3.7.5上,其他方面相同)

相关问题 更多 >

    热门问题