1、传递参数一般有两种方式:引用和传递值。
严格的定义不上了,口语表达为:
引用:子函数直接修改所在地址的值
值传递:将子函数的返回值复制出来并返回
举例:使用过C++的朋友应该知道Cpp中对引用传参和值传参的定义,如 int(a) 和 void(&a)的区别:
值传参是将局部函数中的值复制成一个新的地址传递到主函数中,而引用传参是将主函数的参数地址直接给到子函数,子函数在该内存地址上对内存的值直接操作,举例如下:
如下的C++语句:
#include <iostream>
using namespace std;
int main()
{
int plus1(int);
void plus2(int &);
int b=1;
cout<<" plus1" <<endl;
plus1(b);
//运行不改变b的值
cout<<"plus1 run b= "<<b<<endl;
cout<<" plus2 "<<endl;
plus2(b);
//直接改变b的值
cout<<"plus2 run b= "<<b<<endl;
plus2(b);
//直接改变b的值
cout<<"plus2 run b= "<<b<<endl;
}
int plus1(int a){
a+=1;
return a;
}
void plus2(int&a){
a=a+1;
}
在上面的代码块中,int plus1() 用的是值传递参数,void plus2() 用的是引用传递参数。运行的结果:
plus1
plus1 run b= 1
plus2
plus2 run b= 2
plus2 run b= 3
Process returned 0 (0x0) execution time : 0.062 s
Press any key to continue.
可以看到,在运行plus1之后,虽然有返回值,但是主函数中的b是不改变的,因此在plus1()运行后,b仍然是1,但是在plus2运行之后,b的值直接加上了1,plus2用的就是引用的传参特性。
总结:引用传参直接改变参数的值,值传参不改变主函数中参数的值。
当然,如果想让plus1()也有加1的功能也很简单。将主函数第5行改成:
b=plus1(b);
修改后:
整体代码块为:
#include <iostream>
using namespace std;
int main()
{
int plus1(int);
void plus2(int &);
int b=1;
cout<<" plus1" <<endl;
b=plus1(b);
//运行不改变b的值
cout<<"plus1 run b= "<<b<<endl;
cout<<" plus2 "<<endl;
plus2(b);
//直接改变b的值
cout<<"plus2 run b= "<<b<<endl;
plus2(b);
//直接改变b的值
cout<<"plus2 run b= "<<b<<endl;
}
int plus1(int a){
a+=1;
return a;
}
void plus2(int&a){
a=a+1;
}
运行结果为:
plus1
plus1 run b= 2
plus2
plus2 run b= 3
plus2 run b= 4
Process returned 0 (0x0) execution time : 0.031 s
Press any key to continue.
以上是cpp中传参的用法,但是对于python这种解释性语言而言,没有像cpp这样严密的传参方式定义。它的传参方式又是怎么样的呢。
2、python中的引用传参和值传参特性
直接上结论:
对于可变对象,python是用的引用传参,指字典、列表等可变对象
对于不可变对象,python用的值传参,指数字,字符,元组
上一个python中经典的误用:
def fuc(a,b=[]):
b.append(a)
if __name__=="__main__":
b=[]
fuc(2,b)
print (b)
fuc(3,b)
print(b)
该段代码的目的是为了将a中的元素复制到list b 中,代码希望fuc(a,b) 时,输出的结果时b=[a]
即我们希望运行的结果是:
[2]
[ 3]
然而,实际运行时发现结果为:
[2]
[2, 3]
在fuc(3,b)中竟然输出的b还保留了2的值。
这里就是引用传参在作怪,由于python对于列表时引用传参。在调用fuc(2,b)时,b的值已经被改变成[2].因此在再次调用fuc(3,b)时,b的值时在[2]的基础上在末尾加上了3.即 b=[2 3]
而要想避免以上情况,也很简单。将函数改为:
def fuc(a,b=[]):
b.clear()
b.append(a)
print(f'a={a}')
print(f'b={b}')
print(id(b),'in fuc ')
if __name__=="__main__":
b=[]
print(id(b),'before fuction')
fuc(2,b)
print(f'b in main fuction 1: {b}')
print(id(b),'in main fuction 1')
fuc(3,b)
print(f'b in main fuction 2: {b}')
print(id(b),'in main fuction 2')
即在每次的调用中,都先将b clear 掉。
查看,在子函数中地址和主函数的b的地址:
1783220742600 before fuction
a=2
b=[2]
1783220742600 in fuc
b in main fuction 1: [2]
1783220742600 in main fuction 1
a=3
b=[3]
1783220742600 in fuc
b in main fuction 2: [3]
1783220742600 in main fuction 2
发现b的内存地址在fuc 操作后仍然保持一致,即fuc中的操作是直接修改主函数中b的值
而对于符号变量,python使用的是值传参
def fuc2(a):
print(id(a),'in fuc2')
a=3
print(id(a),'in fuc2 after')
return a
if __name__=="__main__":
a=0
print(id(a),'in main function')
fuc2(a)
运行结果:
1359440928 in main function
1359440928 in fuc2
1359441024 in fuc2 after
发现在 fuc2操作后,a的内存地址是在局部函数fuc2中新开辟的。
a
Out[28]: 0
a没有改变。