Python类成员初始化

时间:2009-05-15 06:17:50

标签: python class initialization

我最近刚刚在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__构造函数中。然而,这让我想知道:

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

  4. 编辑:根据答案我现在明白,通过声明一个数据成员而不是在__init__或其他地方作为self.dict1引用它,我实际上定义了在C ++ / Java中所谓的静态数据成员。通过调用self.dict1我将它设为“实例绑定”。

5 个答案:

答案 0 :(得分:52)

你一直称之为bug是documented,Python类的标准行为。

如您最初所做的那样在__init__之外声明一个字典是声明一个类级变量。它最初只创建一次,每当你创建新对象时,它将重用这个相同的dict。要创建实例变量,请在self中使用__init__声明它们;就这么简单。

答案 1 :(得分:2)

@Matthew:请查看面向对象编程中类成员和对象成员之间的区别。出现此问题是因为原始字典的声明使其成为类成员,而不是对象成员(原始海报的意图。)因此,它存在一次(对于所有类的实例共享)(即一次)对于类本身,作为类对象本身的成员)所以行为是完全正确的。

答案 2 :(得分:1)

当你访问实例的属性时,比如说,self.foo,python将首先在self.__dict__中找到'foo'。如果没有找到,python会在TheClass.__dict__

中找到'foo'

在您的情况下,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