Python类列表在类实例初始化时应该是空的,但事实并非如此。为什么?

时间:2010-12-26 22:25:40

标签: python list initialization

我想创建一个类的实例,其中包含默认为空的列表;而不是以后将此列表设置为最终完整列表,我想连续添加项目。这是一段示例代码:

#!/usr/bin/python

class test:
    def __init__(self, lst=[], intg=0):
        self.lista   = lst
        self.integer = intg

name_dict = {}
counter   = 0

for name in ('Anne', 'Leo', 'Suzy'):
    counter += 1

    name_dict[name] = test()
    name_dict[name].integer += 1
    name_dict[name].lista.append(counter)

    print name, name_dict[name].integer, name_dict[name].lista

当我运行上述程序时,我希望得到

  

安妮1 [1]
  狮子座1 [2]
  Suzy 1 [3]

因为我假设lista始终被初始化为空列表。

我得到的是:

  

安妮1 [1]
  狮子座1 [1,2]
  Suzy 1 [1,2,3]

如果我将self.lista = lst替换为self.lista = [],它可以正常工作,就像我将行name_dict[name].lista = []添加到for循环一样。

为什么保留先前对象列表的内容,但integer的值不是?我对Python很陌生,所以如果有人可以向我指出我的想法/假设误入歧途会很棒。

提前感谢您的回复。

3 个答案:

答案 0 :(得分:12)

使用可变对象作为默认值是一个非常糟糕的主意,就像在此处一样:

def __init__(self, lst=[], intg=0):
     # ...

将其更改为:

def __init__(self, lst=None, intg=0):
     if lst is None:
         lst = []
     # ...

你的版本不起作用的原因是在定义函数时只创建一个空列表,而不是每次调用函数时都会创建。

在某些Python实现中,您可以通过检查func_defaults的值来查看函数默认值的值:

print test.__init__.func_defaults
name_dict[name] = test()
# ...

输出:

([],)
Anne 1 [1]
([1],)
Leo 1 [1, 2]
([1, 2],)
Suzy 1 [1, 2, 3] 

答案 1 :(得分:4)

问题出在这一行:

def __init__(self, lst=[], intg=0):

您不应将列表用作默认参数。在没有指定__init__的情况下调用第一次lst时,Python解释器将定义一个空列表[]。如果未指定lst,则对该功能的后续调用将在相同列表上运行,而不会声明新列表。这会导致奇怪的问题。

您应该使用默认值None并在函数开头添加一个检查:

def __init__(self, lst=None, intg=0):
    if lst is None:
        lst = []

有关详细信息,请参阅this post。引用帖子:

  

默认参数的计算结果为   功能定义时间,所以他们是   持续不断的通话。这有一些   有趣(和令人困惑)的一面   效果。一个例子:

>>> def foo(d=[]):
...     d.append('a')
...     return d
  

如果   你之前没试过这个   可能期望foo总是回归   ['a']:它应该以空的开头   列表,附加'a'并返回。   这是它实际做的:

>>> foo() ['a']
>>> foo() ['a', 'a']
>>> foo() ['a', 'a', 'a']
  

这是因为d的默认值是   当函数被分配时   创建,而不是在它被调用时。每   调用函数的时间,值   从最后一直到处闲逛   呼叫。如果你这更加怪异   把线程扔进混合中。如果两个   不同的线程正在执行   同时起作用,其中之一   他们改变了默认参数   两者都会看到变化。

     

当然,所有这一切都是真的,如果   默认参数的值是a   可变类型。如果我们将foo更改为   定义为

>>> def foo2(d=0):
...     d += 1
...     return d
  

然后它将始终返回1。   (这里的区别在于foo2,   变量d正在重新分配,   而在foo中它的价值正在变化   改变。)

答案 2 :(得分:1)

问题在于默认的构造函数参数。您应该阅读此问题以找到问题的答案:“Least Astonishment” in Python: The Mutable Default Argument