小心此坑:Python 函数参数的默认值是可变对象
source link: https://www.51cto.com/article/722610.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
小心此坑:Python 函数参数的默认值是可变对象
看到了有给 Python 函数参数的默认值传递可变对象,以此来加快斐波那契函数的递归速度,代码如下:
def fib(n, cache={0: 0, 1: 1}):
if n not in cache:
cache[n] = fib(n - 1) + fib(n - 2)
return cache[n]
是不是很新奇,居然可以这样,速度真的非常快,运行结果如下:
不过,我劝你不要这样做,而且 IDE 也会提示你这样做很不好:
这是因为,万物皆对象,Python 函数也是对象,参数的默认值就是对象的属性,在编译阶段参数的默认值就已经绑定到该函数,如果是可变对象,Python 函数参数的默认值在会被存储,并被所有的调用者共享,也就是说,一个函数的参数默认值如果是一个可变对象,例如 List、Dict,调用者 A 修改了它,那么之后调用者 B 在调用的时候看到的就是 A 修改后的结果,这样的模式往往会产生意想不到的结果,比如上面 fib 的算法,但更多的是 bug。
可以看下这段简单的代码:
def func(n, li = []):
for i in range(n):
li.append(i)
print(l)
func(2) # [0,1]
func(3,l=[1,2]) # [1,2,0,1,2]
func(2) # [0,1]
你可以先估算一下这段代码的输出,如果和注释中的一样,那你就错了。正确的结果是:
[0, 1]
[1, 2, 0, 1, 2]
[0, 1, 0, 1]
你可能会觉得,最后一个 func(2) 怎么是这样,不急,我们 print(id(li)) 调试一下:
def func(n, li = []):
print(id(li))
for i in range(n):
li.append(i)
print(li)
func(2)
func(3,li=[1,2])
func(2)
结果如下:
140670243756736
[0, 1]
140670265684928
[1, 2, 0, 1, 2]
140670243756736
[0, 1, 0, 1]
有没有发现,第一个 func(2) 和第二个 func(2) 的 id 是一样的,说明它们用到的是 li 是同一个,这就参数的默认值是可变对象的逻辑,对于所有的调用者来讲,是共享的。
如果要深入研究 Python 为什么这么设计,可以移步 http://cenalulu.github.io/python/default-mutable-arguments/
如何避免?
最好的方式是不要使用可变对象作为函数默认值。如果非要这么用的话,下面是一种解决方案:
def generate_new_list_with(my_list=None, element=None):
if my_list is None:
my_list = []
my_list.append(element)
return my_list
这样,如果 my_list 默认值永远都是 []。
我想那个 fib 函数的实现可能会让你印象深刻,不过请注意,这样的用法非常危险,不可用于自己的代码中。
Recommend
-
36
Python语句中可以进行函数调用来简化工作,每个函数都可以完成具体的任务。当遇到相同任务,调用函数方便快捷。函数首先需设定形参(抽象概念),最后赋予具体值(实参),有些具体值可以不变,则可提前设定好默认值。eg.编写一个和T-SHIRT尺码及标语相关的函数:...
-
42
1.概要 本文描述一个通过C++可变参数模板实现C++反射机制的方法。该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能。Nebula框架在coding.net的仓库地址。 C++11的新特性--可变模版参数(variadictemplates)是C++11新增...
-
46
-
1
python 中的可变参数和不可变参数 2017-03-30 关于此文的部分内容有些混乱与错误,请看
-
6
V2EX › C++ 如何将 list 的值传给函数的可变参数? icemanpro · 2 小时 49 分钟前 · 27...
-
5
pg表级参数修改后怎么简单恢复默认值? ...
-
6
Python函数参数默认值的陷阱和原理深究 本文将介绍使用mutable对象作为Python函数参数默认值潜在的危害,以及其实现原理和设计目的 我们就用实际...
-
5
Python 系列:小心默认的可变参数 2021-11-14 488 words python
-
1
3.8 Go语言中函数可变参数(Variadic Parameter) 2022-07-07 约 1085 字 预计阅读 3 分钟 次阅读 在Python中,在函数参数不确定数量的情况下,可以使用如下方式动态在函数内获取参数,args实质上是一个list,而kw...
-
6
1 个月前发表Rust / Note5 分钟读完 (大约817个字)3 次访问
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK