Python 类成员初始化

问题描述 投票:0回答:5

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

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

编辑:根据答案,我现在明白,通过声明一个数据成员,而不是在

__init__
或其他地方将其引用为 self.dict1,我实际上定义了 C++/Java 中所谓的静态数据成员。通过将其称为 self.dict1,我将其设为“实例绑定”。

python class initialization
5个回答
62
投票

您一直提到的错误是记录的,Python 类的标准行为。

正如您最初所做的那样,在

__init__

 之外声明一个字典就是声明一个类级变量。它一开始只创建一次,每当您创建新对象时,它都会重用同一个字典。要创建实例变量,您可以在 
self
 中使用 
__init__
 声明它们;就这么简单。


2
投票
当您访问实例的属性(例如 self.foo)时,Python 将首先在

self.__dict__

 中查找“foo”。如果没有找到,python 会在 
TheClass.__dict__
 中查找 'foo'

在你的情况下,

dict1

属于A类,而不是实例。 


1
投票
@Matthew:请回顾一下面向对象编程中类成员和对象成员之间的区别。发生此问题是因为原始字典的声明使其成为类成员,而不是对象成员(正如原始发布者的意图)。因此,它对于该类的所有实例存在一次(即共享一次)对于类本身,作为类对象本身的成员),因此行为是完全正确的。


0
投票
Python 类声明作为代码块执行,任何局部变量定义(其中函数定义是一种特殊类型)都存储在构造的类实例中。由于 Python 中属性查找的工作方式,如果在实例上找不到属性,则使用类上的值。

这是关于 Python 博客历史的

一篇关于类语法的有趣文章。


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

© www.soinside.com 2019 - 2024. All rights reserved.