Python中函数传入的参数到底是值还是地址

Pyhton中函数传入的参数是变量值还是引用? 说实话,在这之前还没考虑过,因为直接返回就行,也是因为Python的特性, 变量可以指向任何类型的对象,所以就没太在意,等到遇到这道题后才发现是时候学习一下了。题目链接

Python中变量的特性

1
2
3
a = [1,2,3]  # type(a):list
a = (1,2,3) # type(a):tuple
a = 0 # type(a):int

直观上,一个变量可以随时的改变类型,这在C/C++中是不允许的。但是仔细探究Python语言的特性,变量可以看作是一个任何类型的特殊指针(当然在Python中是没有指针一说的,但是我们可以这样理解),它指向的对象没有限制,但是对象是确定的,即它本身可以是任何类型,但是对象的类型是固定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def test1(val):
    val[1]=0
    print('test1:'+str(id(val)))

b1=[0,1,2,3]
print('b1:'+str(id(b1)))
test1(b1)
print(b1)

Output
	b1:2232273138760
	test1:2232273138760
	[0, 0, 2, 3]
	
def test2(val):
    val=[0,2,5]
    print('test2:'+str(id(val)))

b2=[0,1,2,3]
print('b2:'+str(id(b2)))
test2(b2)
print(b2)

Output
	b2:2232273138824
	test2:2232273139144
	[0, 1, 2, 3]

下面着重结合上述的一个例子来讲述Python中函数的传值问题。

  • 先看test1()函数,发现我们把b1传进去,然后对b1[1]进行操作,然后在主程序中输出发现,函数内和函数外操作的对象是同一个(看输出的两个id一样,就表示在内存中存储的位置一样),这不禁让我想到C++中函数传入指针。
  • 在看test2()函数,我们这次直接对b2进行重新赋值,按照上面的分析,按我们应该得到新的b2,但是结果却是大相径庭,id不一样,而且b2的内容没有变化,这是什么情况?

现在,我们综合分析一下,Python中函数的形参到底是什么?先说答案吧,传入的是变量指向的对象的地址。

  • test1()中,当b1传入函数后,现在我们b1指向[0,1,2,3]的首地址,而val也同样指向[0,1,2,3]的首地址,下面的操作实际上并并没有让val指向的地址发生改变,只是修改了[0,1,2,3]对应位置上的值,所以从头至尾,b1val指向同一个对象,你修改了val指向对象对应位置的值,等同于修改了b1指向的对象对应位置的值。
  • test2()中,当b2传入函数后,b2val同样指向相同对象的首地址,这个和上面一样,但是注意这里它是赋值操作,而这一操作改变了val指向的对象,本来val指向[0,1,2,3],现在一赋值,它就指向了[0,2,5]的首地址,这样本次操作,包括后面对val的操作对不会影响b2,可以说正是赋值操作使valb2脱离了关系。

所以说,函数中对参数做的实际操作,决定了你是改变形参指向的对象(test1()),还是改变新生成的传入参数的副本(test2())。