Python - 入站出站参数

时间:2012-02-09 17:04:42

标签: python memory-management reference keyword-argument

我在专家Python编程中读过这个边缘案例。检查此代码:

def f(arg={}):
    arg['3'] = 4
    return arg

>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4, '4': 'Still here'}

我不清楚为什么最后一次调用f(在保存返回值之后),而不是指定arg空dict(因为它是在没有参数的情况下调用),它保留了老参考。

这本书如此说:“如果在参数中创建了一个对象,那么如果函数返回对象,参数引用仍将是活动的。”

我理解“这是它的工作方式”,但为什么会这样呢?

3 个答案:

答案 0 :(得分:2)

您的问题是默认为可变参数(在本例中为字典):

def f(arg={}):
    arg['3'] = 4
    return arg

应该是:

def f(arg=None):
    arg = arg if arg is not None else {}
    arg['3'] = 4
    return arg

的产率:

>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4}
像你期望的那样。

这里的问题是在首次定义/解析函数时评估默认参数,而不是在调用函数时评估。这只是你需要注意的python解析器的细微差别。

为什么,请查看"Least Astonishment" and the Mutable Default Argument

答案 1 :(得分:2)

因为默认参数仅被评估一次,所以在评估和创建函数时(它们是函数defenition的一部分,例如可以通过inspect.getargspec获取)。

由于它们是函数的一部分,因此对函数的每次调用都将具有默认值的相同实例。如果它是一个不可变的值,这不是问题,但只要它是可变的,它就会成为一个问题。

在课堂辩护中存在相同的“特征”,给定一个类别辩护:

class A(object):
    foo = {}

调用

x = A() 
y = A()
x.foo['bar'] = "baz"

...会给y.foo ['bar']评估为“baz”,因为x和y具有相同的 foo。 这就是成员初始化应该在 init 而不是类体中完成的原因。

答案 2 :(得分:1)

当声明函数时,默认参数被创建一次,因此对f()的每次调用都会获得字典的相同实例,该字典从空开始。这回答了这个问题吗?