Python类成员初始化

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()

我最近刚刚与Python中的一个bug进行了斗争。这是一个愚蠢的新手错误,但它让我想到了Python的机制(我是一个长时间的C++程序员,新的Python)。我将列出错误代码,并解释我做了什么来修复它,然后我有几个问题

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

使用此代码的类为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\u something\u with_aX()
都会初始化类a的一个新的“干净”实例,并在添加之前和之后打印字典

错误(如果您还没有弄清楚):

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

解决这个“错误”的方法显然是添加:

self.dict1 = {}
在类A的
\uuuu init\uuuu
构造函数中。然而,这让我想知道:

  • 在dict1声明点(类A的第一行)“dict1={}”初始化的含义是什么?这是没有意义的吗
  • 什么样的实例化机制会导致从上次初始化复制引用
  • 如果我在构造函数(或任何其他数据成员)中添加“self.dict1={}”,它如何不影响先前初始化实例的dictionary成员


  • 编辑:根据答案,我现在明白了,通过声明一个数据成员,而不是在
    \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