python: 可变参数
结论:定义Python函数的默认参数时,默认参数必须指向不变对象!
事实上,编写程序时如 果可以设计成不变对象,就应该尽量设计成不变对象。
以下例子来自 The Hitchhiker’s Guide to Python .
假如定义函数:
def append_to(element, to=[]):
to.append(element)
return to
调用函数如下:
my_list = append_to(12)
print(my_list)
my_other_list = append_to(42)
print(my_other_list)
我们可能期待这样的结果:
[12]
[42]
实际上得到的是:
[12]
[12, 42]
究其原因,是因为Python默认参数的值在函数定义时即已经算好了,而不会在再次调用时再重新计算。在上述例子中,默认参数to也是一个变量,它指向了对象[],每次函数调用时,如果对象的内容已经改变,那么默认参数to指向内容自然也变了。
对List而言,List在定义时即被创立,只要名字不变,相同的List会一直被后续的调用使用,所以第二次调用时,得到的是已经被改变的List。
想避免上述问题,不使用可变参数即可。
假如真的需要传递一个List,可以使用如下方法:
def append_to(element,to=None):
if to is None:
to=[]
return to.append(element)
×上面表述多少有点易混淆,参数里的to和定义成List的to是否为不同的东西?它们是否指向了不同的地方?如果是,那么不应该使用同样的名字,因为那样容易使人以为两者是相同的。
一些额外的扩展。
这里设计到一个问题,即Python函数的参数是如何传递的?是call-by-name?还是call-by-reference?正确的说两者都不是,Python是call-by-object。
Python有一句话很重要:Everything is an object.
Python中“给一个变量赋值”,这里“变量”更准确的术语是names,“赋值”更准确的表述是“把name与一个object绑定起来”。
请看以下例子体会Python与其他语言的区别:
比如在C++中,
string str=”ABC”;
//…
str=”DEF”;
str指向内存中某一地址,该地址里面存的值是”ABC”;到后面,该内存里的内容被改写,变成”DEF”。
下面看在Python中的情况,
str=’ABC’
#…
str=’DEF’
一开始把name str与一个字符串对象’ABC’绑定;到后面name str与另一个字符串对象’DEF’绑定,但是原先的’ABC’对象并不受影响。
不难理解,一个object可以同时对应多个name。
因此,在Python中无论赋值也好函数调用也好,所做的只是把”作用域”与”对象”绑定起来,如果该对象是可变的,那么它的变化会超出”作用域“之外。
请看下面例子:
def (ele):
ele.append(1)
ele=[2]
m=[]
f(m)
print(m)
结果会是[1]而不是[2],因为List是可变的,append方法改变了m,但是赋值局部变量ele的行为对外面作用域是没有影响的,Python中赋值是给name绑定一个新对象,而不是改变对象。
posted on 2018-11-23 11:33 freshair_cn 阅读(582) 评论(0) 编辑 收藏 举报