其中python函数的默认参数是什么?

Where is the default parameter in Python function

我想很多人都见过接受默认参数的python函数。例如:

1
2
3
def foo(a=[]):
    a.append(3)
    return a

如果我们使用foo()调用这个函数,那么每次调用后输出都会附加整数"3"。

定义此函数时,将在当前环境中定义一个名为"foo"的函数对象,此时还将计算默认参数值。每次调用没有参数的函数时,计算的参数值都会根据代码进行更改。

我的问题是,这个被评估的参数在哪里?调用函数时,它是在函数对象中还是在方法对象中?因为python中的所有内容都是一个对象,所以必须有一些地方来保存"a"->计算参数的name->value绑定。我有没有想过这个问题?


正如其他人所说,默认值存储在函数对象中。

例如,在cpython中,您可以这样做:

1
2
3
4
5
6
7
8
>>> def f(a=[]):
...     pass
...
>>> f.func_defaults
([],)
>>> f.func_code.co_varnames
('a',)
>>>

但是,co_varnames可能包含的参数名称超过个,因此需要进一步处理,而这些属性甚至可能不在其他Python实现中。因此,您应该使用inspect模块来代替它,它为您处理所有的实现细节:

1
2
3
4
5
>>> import inspect
>>> spec = inspect.getargspec(f)
>>> spec
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=([],))
>>>

ArgSpec是一个命名的元组,因此您可以将所有值作为属性访问:

1
2
3
4
5
>>> spec.args
['a']
>>> spec.defaults
([],)
>>>

如文档所述,defaults元组总是对应于args的n个最后参数。这会给你地图。

要创建dict,可以执行以下操作:

1
2
3
>>> dict(zip(spec.args[-len(spec.defaults):], spec.defaults))
{'a': []}
>>>

在函数对象中,在func_defaults中:

1
2
3
4
5
6
7
def f(a=[]): a.append(3)

print f.func_defaults # ([],)

f()

print f.func_defaults # ([3],)


它附加在函数对象上,见foo.func_defaults

1
2
3
4
5
6
>>> foo()
>>> foo.func_defaults
([3],)
>>> foo()
>>> foo.func_defaults
([3, 3],)

如果您想获得a[]的映射,可以访问foo.func_code

1
2
3
4
5
defaults = foo.func_defaults
# the args are locals in the beginning:
args = foo.func_code.co_varnames[:foo.func_code.co_argcount]
def_args = args[-len(defaults):]  # the args with defaults are in the end
print dict(zip(def_args, defaults)) # {'a': []}

(但显然,牦牛的版本更好。)


它存储在函数对象的func_defaults属性中。

1
2
3
4
>>> foo.func_defaults
([],)
>>> foo()
([3],)

我发现了一个有趣的情况:在Python2.5.2版本中,尝试使用函数"foo()"

1
2
3
4
5
6
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]

因为调用函数的对象不同:

1
2
3
4
5
6
>>> id(foo())
4336826757314657360
>>> id(foo())
4336826757314657008
>>> id(foo())
4336826757314683160

在2.7.2版本中:

1
2
3
4
5
6
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

在这种情况下,每次调用函数时对象都是相同的:

1
2
3
4
5
6
>>> id(foo())
29250192
>>> id(foo())
29250192
>>> id(foo())
29250192

这是不同版本的问题吗?