【问题标题】:why python dictionary remembers the value of the previous instance when I initiate a new class instance? [duplicate]为什么python字典在我启动一个新的类实例时会记住前一个实例的值? [复制]
【发布时间】:2012-12-15 17:55:50
【问题描述】:

可能重复:
“Least Astonishment” in Python: The Mutable Default Argument

当我尝试编写如下所示的类时,我遇到了一些很奇怪的事情。在第 3 行,我必须将参数 newdata 的新副本放到 self.data 中,否则,似乎当我启动一个新的类实例时,前一个的值仍然被类记住。请看下面的例子,请注意第 3 行两个版本代码的唯一区别。

class Pt(object):                                                               
    def __init__(self,newdata={}):                                              
        self.data=newdata.copy()                                                       
        if self.data == {}:                                                     
            self._taglist = []                                                  
        else:                                                                   
            self._taglist = self.data.keys()                                    
    def add_tag(self,tag=None):                                                 
        self.data[tag]={'x':[0,1,2,3,4]}                                        
        self._taglist.append(tag)                                               


In [49]: pt = Pt()

In [50]: pt.add_tag('b')

In [51]: pt.add_tag('a')

In [52]: pt.data
Out[52]: {'a': {'x': [0, 1, 2, 3, 4]}, 'b': {'x': [0, 1, 2, 3, 4]}}

In [53]: pt2 = Pt()

In [54]: pt2._taglist
Out[54]: []

class Pt(object):                                                               
    def __init__(self,newdata={}):                                              
        self.data=newdata                                                       
        if self.data == {}:                                                     
            self._taglist = []                                                  
        else:                                                                   
            self._taglist = self.data.keys()                                    
    def add_tag(self,tag=None):                                                 
        self.data[tag]={'x':[0,1,2,3,4]}                                        
        self._taglist.append(tag)                                               

In [56]: pt = Pt()

In [57]: pt.add_tag('a')

In [58]: pt.add_tag('b')

In [59]: pt._taglist
Out[59]: ['a', 'b']

In [60]: pt2 = Pt()

In [61]: pt2._taglist
Out[61]: ['a', 'b']

In [62]: pt2.data
Out[62]: {'a': {'x': [0, 1, 2, 3, 4]}, 'b': {'x': [0, 1, 2, 3, 4]}}

我猜第二种情况的发生是因为:newdata 和 self.data 都引用同一个对象(但怎么会发生这种情况,值应该从右到左而不是反向给出),所以当我使用“ add_tag" 方法来更新 self.data,newdata 也会更新。然而我认为当我通过pt2 = Pt()启动一个新实例时,newdata应该使用默认值({}),它仍然可以保留pt1的旧值吗?

【问题讨论】:

  • 你不应该使用可变对象作为默认值。

标签: python class dictionary instance


【解决方案1】:

“Least Astonishment” in Python: The Mutable Default Argument 中已经很好地解释了原因和一切。所以这里只是一个快速帮助如何解决您的问题。

由于保留了默认参数对象,因此您永远不应使用可变对象作为默认参数值。相反,您应该定义一个固定值,通常是None,作为默认值,然后初始化默认对象。所以你的构造函数看起来像这样:

def __init__(self, newdata=None):
    if newdata is None:
        newdata = {}
    # ...

【讨论】:

  • 我不会说从不使用可变变量作为默认值。有时,这正是您想要的行为。
  • @JerryB 这似乎不太可能,但如果确实如此,您应该使用其他机制来访问共享对象,而不是将其用作默认参数,因为这显然会让人感到困惑。
  • 当然,这肯定很少见,而且我试图给出的任何例子都必然是人为的。但稀有不等于从来没有。就像我在 80 年代上大学的时候,他们一直在敲打“永远不要使用 goto!”但是我还没有处理任何没有 goto 的语言,只是命名了别的东西并具有内置的限制/保护。
  • @JerryB Python 没有 goto。那是除非你调用循环和条件语句 goto 命名别的东西——这会很荒谬,因为跳转是硬件的工作方式,所以你不能不使用它们。
  • 就是这样:every 控制结构实际上是 goto 的一种形式。这确实是重点:您不能使用某种形式的跳跃来完成任何重要的算法。诸如 if 和 for 之类的事情只是迫使您以阅读您的代码的人更容易理解的方式这样做。
【解决方案2】:

您的问题是您复制了字典,然后创建了具有相同值的新字典。这意味着您的字典中的列表是相同的,因此当您修改它们时,它们会在您的所有字典中进行修改。

您要做的是copy.deepcopy() - 这不仅会复制字典,还会复制列表。

【讨论】:

    猜你喜欢
    • 2018-08-26
    • 1970-01-01
    • 2014-08-10
    • 2012-09-27
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    相关资源
    最近更新 更多