我最近刚刚与 Python 中的一个错误作斗争。这是那些愚蠢的新手错误之一,但它让我思考了 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的第二次初始化中,字典不为空,而是从上次初始化的内容开始,依此类推。我希望他们能“重新开始”。
解决这个“bug”的显然是添加:
self.dict1 = {}
在 A 类的
__init__
构造函数中。然而,这让我想知道:
编辑:根据答案,我现在明白,通过声明一个数据成员,而不是在
__init__
或其他地方将其引用为 self.dict1,我实际上定义了 C++/Java 中所谓的静态数据成员。通过将其称为 self.dict1,我将其设为“实例绑定”。
您一直提到的错误是记录的,Python 类的标准行为。
正如您最初所做的那样,在__init__
之外声明一个字典就是声明一个类级变量。它一开始只创建一次,每当您创建新对象时,它都会重用同一个字典。要创建实例变量,您可以在
self
中使用
__init__
声明它们;就这么简单。
self.__dict__
中查找“foo”。如果没有找到,python 会在
TheClass.__dict__
中查找 'foo'
在你的情况下,dict1
属于A类,而不是实例。
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