提到函数参数传递,就不得不了解一下大多数程序语言中以下两种传递方式。
值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。其最大的弊端是传递参数的时候,将创建其拷贝,将拷贝传递给函数,如果传递量大,复制要消耗大量实际和内存。
引用传递:也称为传地址,方法调用时,实际参数的引用(地址,而不是参数的值)被传递给函数中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。
那python中采用哪种参数传递方式呢?
大部分参考文献说,Python参数传递采用的是“传对象的引用”(call-by-object-reference)的方式,即传递的是引用关系而非对象本身。实际上,这种方式相当于传值和传引用的一种综合。这意味着将实参传递给形参后,形参和实参引用了同一对象(相当于给实参值指定了新名称)。
实参和形参最初引用了相同的对象,但赋新值会改变引用关系,param引用了不同的对象,实参则不会发生变化,保持原来的引用。如果被引用的对象是不可变的,那么对象不能做任何改变,当给形参指定新的引用对象时,打破了实参和形参引用同一对象的关联。
当传递可变对象时,
传递了列表,实参和形参都与该对象相关联。但赋值语句在函数中不对paramList进行新的引用,但它改变了对象本身。变量paramList的关联对象不变,但对象的内容变为[100,2,3]。由于argList引用同一对象,因此argList也反映这种变化。不可变对象则不能发生这种变化。
>>> def change_demo(list):
printid(list)
new_list=list
printnew_list
iflen(new_list)>5:
new_list=['a','b','c'] #重新赋值
new_list[2]="xxx" #修改列表中一个元素值
printid(new_list)
printnew_list
第一次测试代码列表长度小于5
>>> test1=[1,2,3,4]
>>> id(test1)
48204232L
>>> change_demo(test1)
48204232
[1, 2, 3, 4]
48204232
[1, 2, 'xxxx', 4]
>>> print test1
[1, 2, 'xxxx', 4] #引用关系不变,对象值改变了
第二次测试代码列表长度大于5
>>> test2=[1,2,3,4,5,6]
>>> change_demo(test2)
48204360
[1, 2, 3, 4, 5, 6]
48139528
['a', 'b', 'xxx'] #关联对象改变了
>>> print test2
[1, 2, 3, 4, 5, 6]
传入test1时,因长度不满足判定条件,new_list(没有重新赋值)未改变指向,仍指向list;传入test2时,列表长度满足判定条件,new_list重新赋值,指向改变,因此list和new_list现在指向不同的实例对象,它们的值是不同的。
传递参数为类实例时,
>>> class Clothes():
... def __init__(self,color,size):
... self.color=color
... self.size=size
...
>>> c1=Clothes('red',180)
>>> def change(c):
... c=Clothes('blue',170)
...
>>> change(c1)
>>> c1.color
'red'
>>>
在函数change中,c1赋值给c时,c也引用c1的引用对象,但代码中c=Clothes('blue',170)又使得c重新引用了新的对象,这样c和c1,引用了不同的对象,c1.color仍输出原来的数据属性。
实际上,java中对一般意义上的传值和传对象引用统称为传值方式,它传的值就是引用。感觉这一点上java和python可以统一起来。
大家以为这个结论对吗?