对Python类和实例变量的复合赋值

7 投票
5 回答
6458 浏览
提问于 2025-04-15 20:18

我一直在尝试理解Python是如何处理类变量和实例变量的。特别是,我发现这个回答非常有帮助。简单来说,它说如果你声明了一个类变量,然后给[instance].property赋值,你实际上是在给一个完全不同的变量赋值——这个变量在一个和类变量不同的命名空间里。

所以我考虑了,如果我想让我的类的每个实例都有一个默认值的成员(比如说零),我应该这样做:

class Foo:
    num = 0

还是这样做呢?

class Foo:
    def __init__(self):
        self.num = 0

根据我之前读到的内容,我觉得第二个例子是在初始化“正确”的变量(也就是实例变量,而不是类变量)。然而,我发现第一种方法也能很好地工作:

class Foo:
    num = 0

bar = Foo()
bar.num += 1 # good, no error here, meaning that bar has an attribute 'num'
bar.num
>>> 1
Foo.num
>>> 0 # yet the class variable is not modified! so what 'num' did I add to just now?

那么……为什么这样也能工作呢?我到底没理解什么?顺便说一下,我之前对面向对象编程的理解主要来自C++,所以通过类比来解释(或者指出哪里出错)可能会很有帮助。

5 个回答

0
bar.num += 1

在这个例子中,'bar'这个实例上创建了一个新的变量'num',因为之前并不存在这个变量(然后这个变量的值加1)。

举个例子:

class Foo:
  def __init__(self):
    self.num= 1

bar = Foo()
print bar.num

这会打印出1

print bar.foo

这会出现一个错误,正如预期的那样:追踪(最近的调用最后): 文件 "", 第1行,在 属性错误:Foo实例没有属性'foo'

bar.foo = 5
print bar.foo

现在这会打印出5

那么在你的例子中发生了什么呢:bar.num被解析为Foo.num,因为只有一个类属性。然后foo.num实际上是被创建了,因为你给它赋了一个值。

2

在下面的代码中,num 是一个类的成员。

class Foo:
    num = 0

在C++中,类似的写法会是这样的:

struct Foo {
  static int num;
};

int Foo::num = 1;

class Foo:
    def __init__(self):
        self.num = 0

self.num 是一个实例成员(selfFoo 的一个实例)。

在C++中,写法会是这样的:

struct Foo {
  int num;
};

我认为Python允许类成员和实例成员使用相同的名字(而C++不允许)。所以当你写 bar = Foo() 时,barFoo 的一个实例,因此用 bar.num += 1 这行代码,你是在增加这个实例成员的值。

5

我个人觉得,Shalabh Chaturvedi写的这些文档在这个话题上非常有用,也很有信息量。

bar.num += 1其实是bar.num = bar.num + 1的简写。这段代码的意思是,它在右边取出类变量Foo.num的值,然后把这个值加1后再赋值给实例变量bar.num

撰写回答