Python:向列表中一个对象的字典添加内容会更改列表中所有其他对象的字典

4

我很乐意为您翻译。以下是需要翻译的内容:

所以Python不是我的强项,我遇到了一个我认为很奇怪的问题。我将问题缩小到了几行代码,以便更容易地提出这个问题。我有一个对象列表,这个对象:

class FinalRecord():
     ruid = 0
     drugs = {}

我在命令行中像这样创建它们:

finalRecords = []
fr = FinalRecord()
fr.ruid = 7
finalRecords.append(fr)
fr2 = FinalRecord()
fr2.ruid = 10
finalRecords.append(fr2)

只要我想更改一个对象上的药品字典,它也会为另一个对象更改。
finalRecords[0].drugs["Avonex"] = "Found"

我打印出了这个:
finalRecords[1].drugs

并且它显示:
{'Avonex':'Found'}

当我期望它实际上是空的时候。我知道我并不完全理解Python如何处理对象,有人可以在这里帮助我吗?

2个回答

5
这是因为drugs是一个类属性。因此,如果您更改了一个对象的属性,则实际上会影响其他对象。
如果您不想出现这种行为,那么您需要使用实例属性。在您的__init__中设置drugs,如下所示:
class FinalRecord():
    def __init__(self):
        self.ruid = 0
        self.drugs = {}

请注意使用self,它是对对象的引用。

关于类属性和实例属性的区别,这里有一些信息:class vs instance attributes

下面是一个完整的演示,说明了这种行为:

>>> class FinalRecord():
...     def __init__(self):
...         self.ruid = 0
...         self.drugs = {}
...
>>> obj1 = FinalRecord()
>>> obj2 = FinalRecord()
>>> obj1.drugs['stuff'] = 2
>>> print(obj1.drugs)
{'stuff': 2}
>>> print(obj2.drugs)
{}

1
啊,我明白了,我之前不太理解 Python 中的类是怎么工作的。 - Spencer Sutton
1
基本上,在类范围(缩进一级)声明的方法(def关键字)和变量是定义字典中的元素,该字典就是你的类。其中一些元素是函数(def),而其他元素是类字段。一旦你理解了Python的动态特性,很多事情就会一下子变得清晰明了。 - Warren P
1
@SpencerSutton:如果这个答案确实帮助你解决了问题,你应该考虑接受它。 - code_dredd
可能已经过去了六年,但是您,先生,仅凭那个简单但重要的答案就从我两天的噩梦中拯救了我。 - Confused Merlin

1
你将 drugs 定义为一个类属性,而不是实例属性。因此,你总是在修改同一个对象。你应该在 __init__ 方法中定义 drugs。我还建议使用 ruid 作为参数:
class FinalRecord():
    def __init__(self, ruid):
        self.ruid = ruid
        self.drugs = {}

它可以被用作这样:
fr = FinalRecord(7)
finalRecords.append(fr)
fr2 = FinalRecord(10)
finalRecords.append(fr2)

或者更简单地说:
finalRecords.append(FinalRecord(7))
finalRecords.append(FinalRecord(10))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接