Python类成员初始化
我最近刚刚与Python中的一个bug进行了斗争。这是一个愚蠢的新手错误,但它让我想到了Python的机制(我是一个长时间的C++程序员,新的Python)。我将列出错误代码,并解释我做了什么来修复它,然后我有几个问题 场景:我有一个名为a的类,它有一个dictionary数据成员,下面是它的代码(当然这是简化): 使用此代码的类为B类:Python类成员初始化,python,class,initialization,Python,Class,Initialization,我最近刚刚与Python中的一个bug进行了斗争。这是一个愚蠢的新手错误,但它让我想到了Python的机制(我是一个长时间的C++程序员,新的Python)。我将列出错误代码,并解释我做了什么来修复它,然后我有几个问题 场景:我有一个名为a的类,它有一个dictionary数据成员,下面是它的代码(当然这是简化): 使用此代码的类为B类: class B: def do_something_with_a1(self): a_instance = A()
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\u something\u with_aX()
都会初始化类a的一个新的“干净”实例,并在添加之前和之后打印字典
错误(如果您还没有弄清楚):
在类A的第二次初始化中,字典不是空的,而是从上次初始化的内容开始,依此类推。我希望他们“重新开始”
解决这个“错误”的方法显然是添加:
self.dict1 = {}
在类A的\uuuu init\uuuu
构造函数中。然而,这让我想知道:
编辑:根据答案,我现在明白了,通过声明一个数据成员,而不是在
\uuu init\uuu
或其他地方将其引用为self.dict1,我实际上是在定义C++/Java中所谓的静态数据成员。通过将其命名为self.dict1,我将其命名为“实例绑定”。您一直称之为bug的是Python类的标准行为
在
\uuuu init\uuuu
之外声明dict就像最初那样,是声明一个类级变量。它一开始只创建一次,无论何时创建新对象,它都将重复使用相同的命令。要创建实例变量,请在\uuuu init\uuuu
中使用self
声明它们;就这么简单。当您访问实例的属性时,比如self.foo,python将首先在self中找到'foo'。如果未找到,python将在类的中找到'foo'
在您的例子中,dict1
属于A类,而不是实例 Pythons类声明作为代码块执行,任何局部变量定义(函数定义是其中的一种特殊类型)都存储在构造的类实例中。由于属性查找在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
@Matthew:请回顾一下面向对象编程中类成员和对象成员之间的区别。发生此问题是因为原始dict的声明使其成为类成员,而不是对象成员(如原始海报的意图)。因此,它只存在一次(共享)类的所有实例(即一次用于类本身,作为类对象本身的成员)因此,这种行为是完全正确的 我没有说这是一个Python错误,我说这是我的错误。。。当一个程序员做了一些违背文档的事情时,它仍然是一个bug(正如我所提到的一个愚蠢的bug)。我的问题是关于触发这种“记录在案的标准行为”的机制——我想了解引擎盖下是什么。谢谢特别是我的第一个问题,我想了解声明初始化是否无用;如果希望一个变量在实例中保持不变,可以使用它。您的问题是,当您执行x=A()时,只有init代码被执行。类变量仍然存在。OK只要声明不在init中,它与C++中的“static”数据成员相同,而“init”中的声明将它从静态变为实例绑定。现在清楚了,谢谢。差不多了。这些文档讲述了它与C++的区别,有些应该检查一下。它可以在任何方法中声明……只是init将始终被执行(除非更改为new)。最好记住的是,python中从来没有任何隐含的“自我”。如果您没有显式地分配给self.member或从self.member读取,则您没有接触到该实例。您应该使用新样式的类,从object派生。类A没有\uuu init\uu()
。您是否故意想要没有,这相当于一个空的def\uuu init\uuu(self):pass
,因此它当然没有数据成员?如果没有,请修复你的代码。这实际上有助于我直观地了解它是如何工作的。因此,一旦将self.x绑定到对象中的某个对象(在init或其他地方),未来的self.x查找将引用实例范围中的新变量。如果他们在实例范围中找不到它,他们会查找类范围。
class ClassA:
dict1 = {}
a = ClassA()
class ClassA:
__defaults__['dict1'] = {}
a = instance(ClassA)
# a bit of pseudo-code here:
for name, value in ClassA.__defaults__:
a.<name> = value
a = {}
b = a
a['foo'] = 'bar'
print b