了解Python样式,函数的默认参数

时间:2015-05-14 21:55:27

标签: python function default-parameters

Python教程涵盖了这一点,但我仍然不太清楚Python具有这种风格的原因。它是纯粹的约定,还是有一些解释为什么Python有关于默认参数的以下样式:

我的理解是,对于函数的默认参数,Python更喜欢something=None而不是something=[]。但是......为什么不使用something=[]?当然这是其他语言的惯例,比如C

举个例子,拿这两个等价的例子

def function(arr, L=[]):
    L.append(arr)
    return L

def function(arr, L=None):
    if L is None:
        L = []
    L.append(arr)
    return L

我的理解是,第一个是"不正确的风格"对于Python。为什么呢?

编辑:啊,我终于明白了。我上面的说法不正确:这两个函数不相同。定义函数时,默认参数计算一次,而不是每次调用函数时!

4 个答案:

答案 0 :(得分:4)

当您将参数设置为列表的值时,会在定义函数时分配该参数,而不是在调用该函数时分配。这就是为什么使用相同的输入参数多次调用函数会得到不同的结果。当心!!!

def function(arr, L=[]):
    L.append(arr)
    return L

arr = [1, 2, 3]

>>> function(arr)
[[1, 2, 3]]

>>> function(arr)
[[1, 2, 3], [1, 2, 3]]

答案 1 :(得分:2)

每次调用时,一个空列表的默认值将引用一个特定变量,而不是每次都创建一个新的空列表。

>>> def add(defList=[]):
        defList.append(1)
        return defList
>>> add()
[1]
>>> add()
[1,1]

这是一个关于Python中可变数据如何工作的怪癖。它偶尔会有用,但使用None通常会更安全。如果没有传递值,则创建一个空列表。

答案 2 :(得分:2)

原因是Python存储L的值。换句话说,L是常量的引用。但常数可以更新

你可以说Python将其存储为:

               |-->  []
               |
function (arr, L)

但是[]是一个普通的对象(因此有状态),也可以修改。现在,如果函数可以修改或返回L ,则开始修改L的状态。在示例中:

def function(arr, L=[]):
    L.append(arr)
    return L

修改L 。如果你第一次调用它(例如使用function(123)),则对象会更新,现在它表示为:

               |-->  [123]
               |
function (arr, L)

因此function的行为取决于全局状态。一般来说,全局状态在代码设计中被视为难闻的气味,而且它不是人们可能期望的。这不适用于None,因为您修改了本地引用L(而不是对象本身)。

您可以说对象是相同的,但每次调用该函数时,将引用复制到局部变量L

现在是第二种情况:

                  |--> None
                  |
def function(arr, L):
    if L is None:
        L = []
    L.append(arr)
    return L

如果您调用此方法,则为L分配值,但L本身不是全局的(仅L引用的对象)。因此,如果您在if之后调用此方法,则函数(正在进行中)将如下所示。

答案 3 :(得分:1)

因为当您调用函数时,参数l被赋予初始值[] NOT ,但是当它首次被声明并且它所在的模块被解释时。

请参阅:Python: Common Gotcha感谢@ferhat elmas

示例:显示正在进行的操作

$ python
Python 2.7.9 (default, Mar 19 2015, 22:32:11) 
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(L=[]):
...     L.append(1)
...     return id(L), L
... 
>>> def g(L=None):
...     L = [] if L is None else L
...     L.append(1)
...     return id(L), L
... 

我们将忽略将另一个列表传递给L参数时会发生什么,因为该行为已明确定义且可接受。 (获取一个列表,附加到它并将其返回)。

>>> f()
(139978918901088, [1])
>>> g()
(139978918964112, [1])

我们第一次拨打f()g()时,我们可能会错误地认为我们已成功从最初的空列表中返回一个新列表了吗?当我们再次调用f()与正确的g()相比时会发生什么:

>>> f()
(139978918901088, [1, 1])
>>> g()
(139978918964112, [1])

因此,为参数L指定的初始值只设置一次,并且仅在定义函数时设置;不是在它被召唤的时候。这是一个常见的问题,如链接aove中所述。

NB: id()这里返回对象的唯一标识,这样您就可以清楚地看到为什么第一个是为定义可变对象(如列表)的默认值的错误方法。

另请注意:在上面的示例中,调用Lf()的身份不会发生变化。