Python类成员初始化

我刚刚在Python中遇到了一个bug。 这是那些愚蠢的新手bug之一,但它让我想到了Python的机制(我是一个很长时间的C ++程序员,是Python的新手)。 我将列出错误的代码,并解释我做了什么来解决它,然后我有几个问题…

场景:我有一个名为A的类,它有一个字典数据成员,下面是它的代码(这当然是简化):

class A: dict1={} def add_stuff_to_1(self, k, v): self.dict1[k]=v def print_stuff(self): print(self.dict1) 

使用此代码的类是B类:

 class B: def do_something_with_a1(self): a_instance = A() a_instance.print_stuff() a_instance.add_stuff_to_1('a', 1) a_instance.add_stuff_to_1('b', 2) a_instance.print_stuff() def do_something_with_a2(self): a_instance = A() a_instance.print_stuff() a_instance.add_stuff_to_1('c', 1) a_instance.add_stuff_to_1('d', 2) a_instance.print_stuff() def do_something_with_a3(self): a_instance = A() a_instance.print_stuff() a_instance.add_stuff_to_1('e', 1) a_instance.add_stuff_to_1('f', 2) a_instance.print_stuff() def __init__(self): self.do_something_with_a1() print("---") self.do_something_with_a2() print("---") self.do_something_with_a3() 

请注意,每次调用do_something_with_aX()初始化类A的新“干净”实例,并在添加之前和之后打印字典。

错误(如果你还没有弄明白的话):

 >>> b_instance = B() {} {'a': 1, 'b': 2} --- {'a': 1, 'b': 2} {'a': 1, 'c': 1, 'b': 2, 'd': 2} --- {'a': 1, 'c': 1, 'b': 2, 'd': 2} {'a': 1, 'c': 1, 'b': 2, 'e': 1, 'd': 2, 'f': 2} 

在类A的第二次初始化中,字典不是空的,而是从上一次初始化的内容开始,依此类推。 我期望他们开始“新鲜”。

什么解决这个“错误”显然是增加:

 self.dict1 = {} 

在类A的__init__构造函数中。但是,这使我想知道:

  1. dict1声明(A类中的第一行)处的“dict1 = {}”初始化是什么意思? 这是没有意义的?
  2. 什么是实例化的机制,导致复制上次初始化的引用?
  3. 如果我在构造函数(或任何其他数据成员)中添加“self.dict1 = {}”,它如何不影响以前初始化实例的字典成员?

编辑:在答案之后,我现在明白,通过声明一个数据成员,而不是在__init__或其他地方作为self.dict1引用它,我实际上定义什么是在C / Java中调用一个静态数据成员。 通过调用它self.dict1我使它“实例绑定”。

你一直提到的bug是Python类的logging的标准行为。

像最初一样在__init__之外声明一个字典是声明一个类级别的variables。 它只是在一开始创build一次,每当你创build新的对象时,它将重用这个相同的字典。 要创build实例variables,可以在__init__中用self声明它们; 就这么简单。

@Matthew:请复习面向对象编程中的类成员和对象成员之间的区别。 这个问题是由于原始字典的声明使其成为类成员,而不是对象成员(因为是原始的发布者的意图)。因此,对于类的所有实例(即共享一次)对于类本身,作为类对象本身的成员),所以行为是完全正确的。

当你访问实例的属性时,比如说self.foo,python会首先在self.__dict__find'foo'。 如果没有find,python会在TheClass.__dict__find'foo' TheClass.__dict__

在你的情况下, dict1是类A,而不是实例。

Pythons类声明作为代码块执行,任何局部variables定义(其中的函数定义是一种特殊types)都存储在构造的类实例中。 由于属性查找的方式在Python中有效,如果在实例上找不到属性,则使用该类的值。

这是一篇关于 Python博客历史上的类语法的有趣文章 。

如果这是你的代码:

 class ClassA: dict1 = {} a = ClassA() 

那么你可能预期这发生在Python内部:

 class ClassA: __defaults__['dict1'] = {} a = instance(ClassA) # a bit of pseudo-code here: for name, value in ClassA.__defaults__: a.<name> = value 

据我所知,这发生了什么事情,除了一个dict的指针复制,而不是值,这是Python中的任何地方的默认行为。 看看这个代码:

 a = {} b = a a['foo'] = 'bar' print b