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

Compound assignment to Python class and instance variables

我一直试图理解Python对类和实例变量的处理。特别是,我发现这个答案非常有用。基本上说,如果您声明一个类变量,然后对[instance].property进行赋值,那么您将完全分配给一个不同的变量——一个位于与类变量不同的命名空间中。

所以我考虑——如果我希望类的每个实例都有一个具有一些默认值(比如零)的成员,我应该这样做吗:

1
2
class Foo:
    num = 0

或者像这样?

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

根据我之前读到的内容,我认为第二个示例将初始化"right"变量(实例而不是类变量)。但是,我发现第一种方法也非常有效:

1
2
3
4
5
6
7
8
9
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?

所以…为什么会这样?我没有得到什么?FWW,我以前对OOP的理解来自C++,所以类比的解释(或者指向它的崩溃)可能是有用的。


就我个人而言,我发现沙拉布·查图维迪的这些文件对于这一主题非常有用和有用。

bar.num += 1bar.num = bar.num + 1的简写。这是在右侧提取类变量Foo.num,并将其分配给实例变量bar.num


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

1
2
class Foo:
    num = 0

一个C++等价物会是一个类似的东西。

1
2
3
4
5
struct Foo {
  static int num;
};

int Foo::num = 1;
1
2
3
class Foo:
    def __init__(self):
        self.num = 0

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

在C++中,它会有点像

1
2
3
struct Foo {
  int num;
};

我相信Python允许你有一个类成员和一个实例成员共享同一个名字(C++没有)。所以当你做bar = Foo()时,barFoo的一个实例,所以用bar.num += 1增加实例成员。


为了寻找这个问题,这个stackoverflow问题和guido van rossum(1,2)的两张(相当旧,但有效)幻灯片都出现在了较高的位置。guido的幻灯片中指出,这种行为与python访问属性的搜索顺序有关(在上面的示例中为num)。我想把这两个放在一起会很好的:)


1
bar.num += 1

在"bar"实例上创建一个新的实例变量"num",因为它尚不存在(然后向该值添加1)

一个例子:

1
2
3
4
5
6
class Foo:
  def __init__(self):
    self.num= 1

bar = Foo()
print bar.num

这张照片1张

1
print bar.foo

这将按预期给出错误:回溯(最近调用的最后一次):文件",第1行,inattributeError:foo实例没有属性"foo"

1
2
bar.foo = 5
print bar.foo

现在这张照片5

所以在您的示例中会发生什么:bar.num被解析为foo.num,因为只有一个class属性。然后会实际创建foo.num,因为您为其指定了一个值。


我想你刚刚在那里发现了一个Python的虫子。bar.num+=1应该是一个错误,相反,它正在对象栏中创建一个与foo.num不同的属性num。

这真是一种奇怪的行为。