Python Class Members Initialization
我最近刚在Python中与一只虫子作斗争。这是一个愚蠢的新手错误,但它让我想到了Python的机制(我是一个长时间的C++程序员,新的Python)。我将列出错误代码并解释我是如何修复的,然后我有几个问题…
场景:我有一个名为a的类,它有一个字典数据成员,下面是它的代码(这当然是简化的):
1 2 3 4 5 6 7 8 | class A: dict1={} def add_stuff_to_1(self, k, v): self.dict1[k]=v def print_stuff(self): print(self.dict1) |
使用此代码的类是B类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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() |
注意,每个对
这个bug(如果你还没有发现的话):
1 2 3 4 5 6 7 8 9 | >>> 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的第二次初始化中,字典不是空的,而是从上次初始化的内容开始,依此类推。我希望他们开始"新鲜"。
解决这个"错误"的办法显然是增加:
1 | self.dict1 = {} |
在类A的
编辑:根据答案,我现在知道,通过声明一个数据成员,而不是在EDCOX1 1中或其他地方作为SELF.DIS1引用它,我实际上定义了C++/Java中所谓的静态数据成员。通过将其命名为self.dict1,我将其命名为"实例绑定"。
您一直提到的bug是有文档记录的、标准的Python类行为。
像最初那样在
@马修:请回顾一下面向对象编程中类成员和对象成员的区别。这个问题的发生是因为原始dict的声明使它成为类成员,而不是对象成员(正如原始海报的意图一样),因此,它对于类的所有实例(即类本身,作为类对象本身的成员)都存在一次(共享一次),所以行为是完全正确的。
当您访问实例的属性时,比如self.foo,python将首先在
在你的例子中,
如果这是您的代码:
1 2 3 | class ClassA: dict1 = {} a = ClassA() |
然后您可能期望这发生在python中:
1 2 3 4 5 6 7 | class ClassA: __defaults__['dict1'] = {} a = instance(ClassA) # a bit of pseudo-code here: for name, value in ClassA.__defaults__: a.<name> = value |
据我所知,这就是所发生的事情,只是
1 2 3 4 | a = {} b = a a['foo'] = 'bar' print b |
pythons类声明作为代码块执行,任何局部变量定义(函数定义是一种特殊类型)都存储在构造的类实例中。由于属性查找在Python中的工作方式,如果在实例上找不到属性,则使用类上的值。
这是一篇关于Python博客历史上的类语法的有趣文章。