我最近刚刚在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类的第二次初始化中,字典不是空的,而是从上次初始化的内容开始,依此类推。我希望他们开始“新鲜”。
解决这个“错误”的原因显然是添加:
self.dict1 = {}
在A类的__init__
构造函数中。然而,这让我想知道:
__init__
或其他地方作为self.dict1引用它,我实际上定义了在C ++ / Java中所谓的静态数据成员。通过调用self.dict1我将它设为“实例绑定”。
答案 0 :(得分:52)
你一直称之为bug是documented,Python类的标准行为。
如您最初所做的那样在__init__
之外声明一个字典是声明一个类级变量。它最初只创建一次,每当你创建新对象时,它将重用这个相同的dict。要创建实例变量,请在self
中使用__init__
声明它们;就这么简单。
答案 1 :(得分:2)
@Matthew:请查看面向对象编程中类成员和对象成员之间的区别。出现此问题是因为原始字典的声明使其成为类成员,而不是对象成员(原始海报的意图。)因此,它存在一次(对于所有类的实例共享)(即一次)对于类本身,作为类对象本身的成员)所以行为是完全正确的。
答案 2 :(得分:1)
当你访问实例的属性时,比如说,self.foo,python将首先在self.__dict__
中找到'foo'。如果没有找到,python会在TheClass.__dict__
在您的情况下,dict1
属于A类,而不是实例。
答案 3 :(得分:0)
Pythons类声明作为代码块执行,任何局部变量定义(其函数定义是特殊类型)都存储在构造的类实例中。由于在Python中查找属性查找方式,如果在实例上找不到属性,则使用该类上的值。
关于Python博客历史的an interesting article about the class syntax。
答案 4 :(得分:0)
如果这是您的代码:
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