tyltr技术窝

python作为一种脚本语言以其灵活性、易上手而著称。甚至一度被人称为“可执行的伪代码”。虽然如此,但python里确实有很多坑。

默认参数坑死人#

对于下面的代码,相信大家都不会陌生

例子1

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding: UTF-8 -*-
# author: tyltr

def A(item, l=[]):
l.append(item)
return l


if __name__ == '__main__':
print(A(1))
print(A(1))
print(A(1))
print(A(1))

输出:

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

为啥呢?
有人说这是由于传入的是list造成的,因为list的可变的。
那么看下一个例子
例子2

1
2
3
4
5
6
7
8
9
10
def A(item, l={}):
l[item] = 1
return l


if __name__ == '__main__':
print(A(1))
print(A(2))
print(A(3))
print(A(4))

输出:

1
2
3
4
{1: 1}
{1: 1, 2: 1}
{1: 1, 2: 1, 3: 1}
{1: 1, 2: 1, 3: 1}

又为啥呢?是不是有点动摇了。

那就再来一个例子
例子3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class W:
name = ""


def A(item, l=W()):
print(id(l))
l.name += item
return l.name


if __name__ == '__main__':
print(A("1"))
print(A("2"))
print(A("3"))
print(A("4"))

输出什么?

1
2
3
4
1
12
123
1234

原因其实是指针传递的问题,而不是很多博客或者文章里面写的“ list是可变类型的原因,tuple不可不变所以不会产生这个原因 ”。

在python中,默认参数其实是引用拷贝(传递的是指针)。以例子1说明,在创建函数的时候,参数中有默认参数。python解释器会创建一个包含默认值的地址空间,并把这个空间的引用(指针)作为参数传递给函数。这样做可以复用默认值存储空间,减少资源的消耗。但同时会带来一问题。也就是我们本文描述的问题。因为每次修改都会修改这个地址指向的空间,所以下次复用的时候,内容已经发送改变。

这个问题虽然简单,但很多注水文章很误人子弟。话痨了。
这篇博文仓促写成,如有错误,望请指正。谢谢