Python默认参数的设计陷阱

2017-01-13 10:56:55来源:csdn作者:u012393791人点击

关于Python的默认参数,发现了一个很奇怪的现象,看如下代码:

def f(a=[]):
a.append("xyz")
return aprint(f())
print(f())print(f([]))
print(f([]))print("*************")def f1(a=1):
a=a+1
return aprint(f1())
print(f1())print(f1(2))
print(f1(2))

运行结果如下:

['xyz']
['xyz', 'xyz']
['xyz']
['xyz']
*************
2
2
3
3

可以发现: 函数f(a)的有参调用f([])是没有问题的,但是无参调用f()是有问题的:两次调用的结果应该都是[‘xyz’]; 函数f1(a)的有参和无参调用都是正常的。

要了解这个问题的原因,首先要知道Python变量的实质:


其他编程语言:申明及赋值方式
Python:创建及指向,类似于指针的方式。先看个简单的例子
a = 8
a = a + 1

对于传统编程语言,上面代码执行方式: 1. 内存中申明一个变量a,a指向一个内存地址如0x0001; 2. 将8存入变量a所指向的内存0x0001; 3. 执行加法操作后得到9的结果,将9这个数值存入并覆盖到a所指向的内存0x0001中。 结论:整个执行过程中,变化的是变量a所指向的内存地址上的值,而a指向的内存地址是不变的

对于Python: 1. 在内存中创建了一个8的对象,假设8的内存地址为0x0002,变量a指向0x0002,即8这个对象; 2. 执行完加法操作后,得到一个值9,也会创建一个9的新对象,内存地址假设为0x0003; 3. 变量a从指向对象8,变成指向对象9,即a指向的是0x0003。 结论:在整个执行过程中,a指向的内存地址是变化的

问题的根本原因 Python Common Gotchas中对改问题的解释如下:

Python’s default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.

Python的官方文档中也有提示:

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

由此可见,Python中函数默认参数值是在代码compile阶段就已经被定义,即指向一个已经定义好的对象,假设内存地址为0x0010。也就是说之后函数调用时,如果是无参调用,那么,默认参数就是那个在compile阶段就已经存在的对象的指针0x0010。下面来分析文章一开始的两个函数的无参调用: 1.函数f1(a=1), 无参调用f1()之前:a指向0x0010,0x0010中的值为1 无参调用f1():a指向2这个对象,假设2对象的地址为0x0011 无参调用f1()结束后:a重新指向0x0010 2.函数f(a=[])数组对象的值是可变的,定义函数f(a=[]) 时,compile阶段在内存分配一个空数组,假设首地址为:0x0020 当无参调用函数f()时,虽然函数内部对数组进行了添加元素的改变,但是跟上面f1()不一样,并没有新建一个数组对象,所以参数a始终指向0x0020这个数组对象,不管调用几次都在处理这个数组,该数组就相当于一个全局数组了。类似于下面一段代码:

b=[]
def f(a=b):
a.append("xyz")
return a
print(f())
print(f())

运行结果:

['xyz']
['xyz', 'xyz']

解决办法

def f(a=None):
if a==None:
a=[]
a.append("xyz")
return aprint(f())
print(f())print(f([]))
print(f([]))

运行结果:

['xyz']
['xyz']
['xyz']
['xyz']

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台