传参一直是语言中有点纠结的东西。一提到这个,总会有人说,需要区分传值,传递引用,还有传递指针什么的。而且,貌似不同的语言对此也有不同的实现。
我自己也对这个有点搞混了,所以需要实验一下。
写在开头:
我常用的几个语言是,C++,Go语言,python这几种。三个语言中,只有C++有引用,而Python是没有指针的。参数传递主要就两类,传值和传递引用。
本文只写Python和Go语言,C++太复杂了,需要专门研究。
试验的主要分为几种类型,分别为:
- 单一的变量类型,比如int,float这种基本类型。实验中使用int。
- 复杂点的变量类型,比如数组,string,有的自带map等。试验中使用数组。
- 复合类型研究,比如class,struct。
Python的传参研究:
貌似python根本就没有指针,引用的概念。所以,方便不少。
但是,这也会带来别的问题,因为问题还是存在,传递参数到函数中,函数内对参数的改变,会不会影响到外面的。
基础变量的传参:
代码:
01 |
#coding=utf-8 |
02 |
03 |
def play(x): |
04 |
x + = 1 |
05 |
print "函数内:" ,x |
06 |
07 |
x = 1 |
08 |
print "初始状态:" ,x |
09 |
play(x) |
10 |
print "执行play函数后:" ,x |
输出:
1 |
初始状态: 1 |
2 |
函数内: 2 |
3 |
执行play函数后: 1 |
基础变量的传参是复制传参,也就是传值。函数内对变量的操作不会影响外部。
数组传参:
代码:
01 |
#coding=utf-8 |
02 |
def play(x): |
03 |
x[ 0 ] + = 1 |
04 |
x[ 1 ] + = 3 |
05 |
print "函数内:" ,x |
06 |
07 |
x = [ 1 , 1 , 1 , 1 ] |
08 |
print "初始状态:" ,x |
09 |
play(x) |
10 |
print "执行play函数后:" ,x |
输出:
1 |
初始状态: [1, 1, 1, 1] |
2 |
函数内: [2, 4, 1, 1] |
3 |
执行play函数后: [2, 4, 1, 1] |
函数内的改变影响了外部。所以这是传递类的引用。不单单是数组,python的dict等类型都是引用传参(它们其实都是类)。所以需要注意。
复合变量传参:
代码:
01 |
#coding=utf-8 |
02 |
class mytest: |
03 |
a = 1 |
04 |
b = 1 |
05 |
06 |
def __repr__( self ): |
07 |
return "a=" + str ( self .a) + ",b=" + str ( self .b) |
08 |
09 |
def play(x): |
10 |
x.a + = 1 |
11 |
x.b + = 3 |
12 |
print "函数内:" ,x |
13 |
14 |
x = mytest() |
15 |
16 |
print "初始状态:" ,x |
17 |
play(x) |
18 |
print "执行play函数后:" ,x |
输出:
1 |
初始状态: a=1,b=1 |
2 |
函数内: a=2,b=4 |
3 |
执行play函数后: a=2,b=4 |
这里,对象被传递到函数中,函数内对实例的操作影响到了外面。所以,这是传递对象的引用。
Go语言的传参研究:
基础变量的传参——传值:
代码:
01 |
package main |
02 |
import "fmt" |
03 |
func add(x int ) { |
04 |
x++ |
05 |
fmt.Println( "函数内的x:" ,x) |
06 |
} |
07 |
func main() { |
08 |
x:=1 |
09 |
fmt.Println( "初始的x:" ,x) |
10 |
add(x) |
11 |
fmt.Println( "函数执行后的x: " ,x) |
12 |
} |
输出:
1 |
初始的x: 1 |
2 |
函数内的x: 2 |
3 |
函数执行后的x: 1 |
看的出来,这是传值,函数内对x的操作并没有影响到外部的x。很明显,调用add函数时,函数copy了一份x。这和python的一样。
基础变量的传参——传递指针:
01 |
package main |
02 |
import "fmt" |
03 |
func add(ptr * int ) { |
04 |
fmt.Println( "------函数内------" ) |
05 |
*ptr++ |
06 |
fmt.Println( "ptr指向的值:" ,*ptr) |
07 |
fmt.Println( "ptr指向的地址:" ,ptr) |
08 |
} |
09 |
10 |
func main() { |
11 |
x:=1 |
12 |
var ptr * int =&x |
13 |
fmt.Println( "初始的x:" ,x) |
14 |
fmt.Println( "ptr指针指向的值:" ,*ptr) |
15 |
fmt.Println( "ptr指向的地址:" ,ptr) |
16 |
add(ptr) |
17 |
fmt.Println( "------函数执行后-----------" ) |
18 |
fmt.Println( "函数执行后的x: " ,x) |
19 |
fmt.Println( "ptr指针的值:" ,*ptr) |
20 |
} |
修改了原来的代码,在主函数声明了一个ptr指针,指向x。然后把指针传递进add函数。
输出:
1 |
初始的x: 1 |
2 |
ptr指针指向的值: 1 |
3 |
ptr指向的地址: 0xc208000150 |
4 |
------函数内------ |
5 |
ptr指向的值: 2 |
6 |
ptr指向的地址: 0xc208000150 |
7 |
------函数执行后----------- |
8 |
函数执行后的x: 2 |
9 |
ptr指针的值: 2 |
执行后,外面的x也变成了2.
可以看出,add函数的修改,影响了外部的x。这也是传值,但是,传递的值是一个地址。
数组变量的传值研究:
代码:
01 |
package main |
02 |
import "fmt" |
03 |
func add(array [] int ) { |
04 |
fmt.Println( "------函数内------" ) |
05 |
array[0]+=1 |
06 |
array[1]+=2 |
07 |
fmt.Println( "函数内的数组:" ,array) |
08 |
} |
09 |
func main() { |
10 |
array:=[] int {1,1,1,1} |
11 |
fmt.Println( "-------初始状态------" ) |
12 |
fmt.Println( "初始的数组:" ,array) |
13 |
add(array) |
14 |
fmt.Println( "------函数执行后-----------" ) |
15 |
fmt.Println( "函数执行后的数组:" ,array) |
16 |
} |
输出:
1 |
-------初始状态------ |
2 |
初始的数组: [1 1 1 1] |
3 |
------函数内------ |
4 |
函数内的数组: [2 3 1 1] |
5 |
------函数执行后----------- |
6 |
函数执行后的数组: [2 3 1 1] |
传递数组和传递指针有着类似的效果,在函数内改变了数组内的值,外面的数组值也改了。这个和c类语言类似。
复合变量传参:
代码:
01 |
package main |
02 |
import "fmt" |
03 |
import "strconv" |
04 |
type Test struct { |
05 |
a int |
06 |
b int |
07 |
} |
08 |
func ( this *Test)str()string{ |
09 |
var tmp string |
10 |
tmp= "a:" +strconv.Itoa( this .a)+ " b:" +strconv.Itoa( this .b) |
11 |
return tmp |
12 |
} |
13 |
14 |
func add(test Test){ |
15 |
test.a+=1 |
16 |
test.b+=2 |
17 |
fmt.Println( "函数中的struct:" ,test.str()) |
18 |
} |
19 |
20 |
func main() { |
21 |
test:=Test{1,10} |
22 |
fmt.Println( "-------初始状态------" ) |
23 |
fmt.Println( "初始的struct:" ,test.str()) |
24 |
25 |
add(test) |
26 |
fmt.Println( "------函数执行后-----------" ) |
27 |
fmt.Println( "函数执行后的struct:" ,test.str()) |
28 |
} |
输出:
1 |
初始的struct: a:1 b:10 |
2 |
函数中的struct: a:2 b:12 |
3 |
------函数执行后----------- |
4 |
函数执行后的struct: a:1 b:10 |
被作为参数传入给函数的结构体test,在add函数里对2个值做了加1和加2操作,但是没有影响到外部的变量。这是传值。
实验总结:
从上面的实验来看,无论是Python还是Go,它们的基础类型在传参时都是复制传参(传值)。对于数组,二者也是一样,类似传递指针,属于传递引用。
但是对于对象,Python参数传递类似于指针传递(传对象引用),Go语言则是类似于C类语言,属于复制传参。
语言 | Python | Go语言 |
基础变量传参 | 传值 | 传值 |
数组 | 传递引用 | 传递引用 |
对象 | 传递引用(arry,dict属于对象) | 传值 |
转载请注明:旅途@KryptosX » 对Python和Go的函数传参研究