当前位置 : 主页 > 编程语言 > python >

Python学习笔记之函数参数传递 传值还是传引用

来源:互联网 收集:自由互联 发布时间:2022-06-15
在学完​​Python​​​函数那一章节时,很自然的的就会想到​​Python​​中函数传参时传值呢?还是传引用?或者都不是? 在回答上面的问题之前我们先来看看下面的代码: 代码1


在学完​​Python​​​函数那一章节时,很自然的的就会想到​​Python​​中函数传参时传值呢?还是传引用?或者都不是? 


在回答上面的问题之前我们先来看看下面的代码:

代码1:

def foo(var):
var = 2
print(var) #output: 2
a = 1
foo(a)
print(a) #output: 1


恩,看似是值传递

代码2:

def bar(var):
var.append(1)

b = []
print(b) #output:[]
bar(b)
print(b) #output:[1]


应该是引用传递?有点奇怪吧,为了弄清楚这个问题,我们先来了解一下​​Python​​中变量与对象的关系。


一、变量和对象

我们首先要知道​​Python​​中的“变量”与​​C/C++​​中“变量”是不同的。

在​​C/C++​​中,当你初始化一个变量时,就是声明一块存储空间并写入值。相当于把一个值放入一个盒子里:

​​int a = 1;​​

Python学习笔记之函数参数传递 传值还是传引用_c++

现在​​a​​盒子里放了一个整数​​1​​,当给变量​​a​​赋另外一个值时会替换盒子​​a​​里面的内容:

​​a = 2;​​

Python学习笔记之函数参数传递 传值还是传引用_python_02

当你把变量​​a​​赋给另外一个变量时,会拷贝​​a​​盒子中的值并放入一个新的“盒子”里:

​​int b = a;​​

Python学习笔记之函数参数传递 传值还是传引用_c++_03Python学习笔记之函数参数传递 传值还是传引用_参数传递_04

但是

在​​Python​​中,一个变量可以说是内存中的一个对象的“标签”或“引用”:

​​a = 1​​

Python学习笔记之函数参数传递 传值还是传引用_c++_05

现在变量​​a​​指向了内存中的一个​​int​​型的对象(​​a​​相当于对象的标签)。如果给​​a​​重新赋值,那么“标签” ​​a​​ 将会移动并指向另一个对象:

​​a = 2​​

Python学习笔记之函数参数传递 传值还是传引用_c++_06

原来的值为​​1​​的​​int​​型对象仍然存在,但我们不能再通过​​a​​这个标识符去访问它了(当一个对象没有任何标签或引用指向它时,它就会被自动释放)。如果我们把变量​​a​​赋给另一个变量,我们只是给当前内存中对象增加一个“标签”而已:

​​b = a​​

Python学习笔记之函数参数传递 传值还是传引用_c++_07

综上所述,在​​Python​​中变量只是一个标签,一个标识符,它指向内存中的对象。故变量并没有类型,类型是属于对象的,这也是​​Python​​中的变量可以被任何类型赋值的原因。

二、可变对象与不可变对象

在​​Python​​的基本数据类型中,我们知道​​numbers​​、​​strings​​和​​tuples​​是不可更改的对象,而​​list​​、​​dict​​是可以修改的对象。那么可变与不可变有什么区别呢?看下面示例:

a = 1 # a指向内存中一个int型对象
a = 2 # 重新赋值


当将​​a​​​重新赋值时,因为原来值为​​1​​​的对象是不能改变的,所以​​a​​​会指向一个新的​​int​​​对象,其值为​​2​​。(如下面的图示)

Python学习笔记之函数参数传递 传值还是传引用_Python_08

示例2

list1 = [1, 2] # list1指向内存中一个list类型的对象
list1[0] = 2 # 重新赋值list1中第一个元素


因为​​list​​​类型是可以改变的,所以第一个元素变更为​​2​​​。更确切的说,​​list1​​​的第一个元素是​​int​​​型,重新赋值时一个新的​​int​​​对象被指定给第一个元素,但是对于​​list1​​来说,它所指的列表型对象没有变,只是列表的内容(其中一个元素)改变了。如下图:

Python学习笔记之函数参数传递 传值还是传引用_python_09

现在我们再来看看开始那两段代码:

def foo(var):
var = 2
print(var)

a = 1
foo(a)
print(a)


上面这段代码把​​a​​作为参数传递给函数,这时​​a​​和​​var​​都指向内存中值为​​1​​的对象。然后在函数中​​var = 2​​时,因为​​int​​对象不可改变,于是创建一个新的​​int​​对象(值为​​2​​)并且令​​var​​指向它。而​​a​​仍然指向原来的值为​​1​​的​​int​​对象,所以函数没有改变变量​​a​​。

如下图:

Python学习笔记之函数参数传递 传值还是传引用_c++_10

代码2;

def Bar(var):
var.append(1)

b = []
print(b)
Bar(b)
print(b)


这段代码把​​b​​传递给函数​​Bar​​,那么​​b​​和​​var​​都会指向同一个​​list​​类型的对象。因为​​list​​对象是可以改变的,函数中使用​​append​​在其末尾添加了一个元素,​​list​​对象的内容发生了改变,但是​​b​​和​​var​​仍然是指向这一个​​list​​对象,所以变量​​b​​的内容也发生了改变。

如下图:

Python学习笔记之函数参数传递 传值还是传引用_python_11

那么​​Python​​​中参数传递是传值,还是传引用呢?准确的回答:都不是。之所以不是传值,因为没有产生复制,而且函数拥有与调用者同样的对象。而似乎更像是​​C++​​的传引用,但是有时却不能改变实参的值。所以只能这样说:对于不可变的对象,它看起来像C++中的传值方式;对于可变对象,它看起来像C++中的按引用传递。


​​参考​​





网友评论