关于 Python 参数默认值

2019-06-16 10:07:25 +08:00
 patrickstar

Python 手册的 Tutorial.pdf 中 “ 4.7.1 参数默认值” 有如下的例子:

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

这将打印出

[1]
[1, 2]
[1, 2, 3]

如果你不想要在后续调用之间共享默认值,你可以这样写这个函数:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

我家高中生刚学编程,把程序改为如下进行测试,问我 f3 和 f1 的 L 到底差别在哪儿,把我搞懵了,哪个能给出一个简单、初学编程的人听得懂的解释(我只能给他解释 f1 的 L 是可变对象,f3 的 L 是不可变对象?)

def f1(a, L=[]):
	print("id(L) = ", id(L))
	print("L = ", L)
	print()
	L.append(a)
	return L

def f3(a, L=None):
	print("id(L) = ", id(L))
	print("L = ", L)
	if L is None:	# 为何每次成立?
		L=[]
		print("id(L) = ", id(L))
		print()
	L.append(a)
	return L


if __name__ == "__main__":
	f1(1)
	f1(2)
	f1(3)

	print('-'*40)
	f3(1)
	f3(2)
	f3(3)

执行时打印出:

id(L) =  2405815737608
L =  []

id(L) =  2405815737608
L =  [1]

id(L) =  2405815737608
L =  [1, 2]

----------------------------------------
id(L) =  1983976656
L =  None
id(L) =  2405815759368

id(L) =  1983976656
L =  None
id(L) =  2405815759368

id(L) =  1983976656
L =  None
id(L) =  2405815759368
1682 次点击
所在节点    问与答
7 条回复
densuc
2019-06-16 10:32:47 +08:00
用的 2.7 版本?好像 2 和 3 版本的变量带入是不同的。。。
BingoXuan
2019-06-16 10:37:52 +08:00
很明显就是函数定义时候这个默认变量就已经初始化了,但调用函数时候,就算重新赋值,也不会修改原变量。也就是函数和其默认参数的作用域是一样。就像你定义一个变量后,再定义函数并传入。你可以用 inspect 模块看一下,你会发现 f1 的默认参数值都会变。其实这个是个坑,我记得某家公司就是被这个特性( bug )搞挂了。
youngce
2019-06-16 10:43:18 +08:00
```python
def f(a, L=[]):
L.append(a)
return L

print(f(1),f.__defaults__) # [1] ([1],)

print(f(2),f.__defaults__) # [1, 2] ([1, 2],)
print(f(3),f.__defaults__) # [1, 2, 3] ([1, 2, 3],)
```
不知道有没有帮助,总之 python 里面没有原生的函数,都是一堆对象
princelai
2019-06-16 10:44:51 +08:00
等号左边的 L 存储的是一个栈地址,当等号右边是可变对象是,赋值为一个可变的堆对象地址,也就是指针,当等号右边是个不可变常量时,存储该值的地址。Python 的参数传递是引用传递,给个可变对象意味着这是外部对象的指针,给个不可变对象,然后你又在内部从新赋值为[],L 一直是内部变量
azh7138m
2019-06-16 10:44:56 +08:00
f1 每次的默认参数都是同一个 '[]'
f3 每次的默认参数都是同一个 'None'

> Python ’ s default arguments are evaluated once when the function is defined, not each time the function is called
Xs0ul
2019-06-16 10:49:09 +08:00
楼主想问的是为什么 f3 运行得到的 list 的 id 还是一样的?

你试试
l1 = f3(1)
l2 = f3(2)
l3 = f3(3)
或者
f3(1)
a = []
print(id(a))
f3(2)
f3(3)

f1 里一样是因为相当于提前创建了一个 list,把这个 list 作为默认参数,所以每次都是这一个

f3 里是内部创建了一个新的 list,按道理每次 id 不一定一样。但是因为生成的 list 没被接收,相当于已经废弃,第二次生成的时候或许是重复利用了 /生成了同样的一个
Takamine
2019-06-16 19:06:15 +08:00
最好不要用可变类型做入参,很容易有坑。(。ò ∀ ó。)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/574374

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX