文档章节

写给小白的Python之012:传值还是传引用?

o
 osc_x4h57ch8
发布于 2018/04/24 07:41
字数 1852
阅读 15
收藏 0

精选30+云产品,助力企业轻松上云!>>>

导读:

1.变量和对象

2.可变对象与不可变对象

3.引用传参

 

在C/C++中,传值和传引用是函数参数传递的两种方式。由于思维定式,从C/C++转过来的Python初学者也经常会感到疑惑:在Python中,函数参数传递是传值,还是传引用呢?
看下面两段代码:

def foo(arg):
arg = 5
print(arg)
x = 1
foo(x) # 输出5
print(x) # 输出1

def foo(arg):
arg.append(3)
x = [1, 2]
print(x) # 输出[1, 2]
foo(x)
print(x) # 输出[1, 2, 3]

看完第一段代码,会有人说这是值传递,因为函数并没有改变x的值;看完第二段代码,又会有人说这是传引用,因为函数改变了x的内容。
那么,Python中的函数到底是传值还是传引用呢?

一、变量和对象

我们需要先知道Python中的“变量”与C/C++中“变量”是不同的。
在C/C++中,当你初始化一个变量时,就是声明一块存储空间并写入值。相当于把一个值放入一个盒子里:
int a = 1;

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

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

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

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

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

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


在python中,值是靠引用来传递来的。
我们可以用id()来判断两个变量是否为同一个值的引用。 我们可以将id值理解为那块内存的地址标示。

>>> a = 1
>>> b = a
>>> id(a) 
13033816
>>> id(b) # 注意两个变量的id值相同
13033816
>>> a = 2
>>> id(a) # 注意a的id值已经变了
13033792
>>> id(b) # b的id值依旧
13033816

>>> a = [1, 2]
>>> b = a
>>> id(a)
139935018544808
>>> id(b)
139935018544808
>>> a.append(3)
>>> a
[1, 2, 3]
>>> id(a)
139935018544808
>>> id(b) # 注意a与b始终指向同一个地址
139935018544808
View Code

 

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

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

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

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

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

因为list类型是可以改变的,所以第一个元素变更为2。更确切的说,lst的第一个元素是int型,重新赋值时一个新的int对象被指定给第一个元素,但是对于lst来说,它所指的列表型对象没有变,只是列表的内容(其中一个元素)改变了。
好了,到这里我们就很容易解释开头的两段代码了:

def foo(arg):
arg = 5
print(arg)
x = 1
foo(x) # 输出5
print(x) # 输出1

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

def foo(arg):
arg.append(3)
x = [1, 2]
print(x) # 输出[1, 2]
foo(x)
print(x) # 输出[1, 2, 3]

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

三、引用传参

可变类型与不可变类型的变量分别作为函数参数时,会有什么不同吗?
Python有没有类似C语言中的指针传参呢?

>>> def selfAdd(a):
... """自增"""
... a += a
...
>>> a_int = 1
>>> a_int
1
>>> selfAdd(a_int)
>>> a_int
1
>>> a_list = [1, 2]
>>> a_list
[1, 2]
>>> selfAdd(a_list)
>>> a_list
[1, 2, 1, 2]

Python中函数参数是引用传递(注意不是值传递)。对于不可变类型,因变量不能修改,所以运算不会影响到变量自身;而对于可变类型来说,函数体中的运算有可能会更改传入的参数变量。

想一想为什么

>>> def selfAdd(a):
... """自增"""
... a = a + a # 我们更改了函数体的这句话
...
>>> a_int = 1
>>> a_int
1
>>> selfAdd(a_int)
>>> a_int
1
>>> a_list = [1, 2]
>>> a_list
[1, 2]
>>> selfAdd(a_list)
>>> a_list
[1, 2] # 想一想为什么没有变呢?

总结:
x += x是直接对x指向的空间进行修改,而不是让b指向一个新的。
x = x+x先把=号右边的结果计算出来,然后让x指向这个新的地方,不管原来b指向谁.

 四、一切皆对象

Python使用对象模型来储存数据,任何类型的值都是一个对象。所有的python对象都有3个特征:身份id类型type值value
身份:每一个对象都有自己的唯一的标识,可以使用内建函数id()来得到它。这个值可以被认为是该对象的内存地址。
类型:对象的类型决定了该对象可以保存的什么类型的值,可以进行什么操作,以及遵循什么样的规则。type()函数来查看python 对象的类型。
:对象表示的数据项。

>>> a = 1
>>> id(a)
140068196051520
>>> b = 2
>>> id(b)
140068196051552
>>> c = a
>>> id(c)
140068196051520
>>> c is a
True
>>> c is not b
True

运算符 is 、 is not 就是通过id()的返回值(即身份)来判定的,也就是看它们是不是同一个对象的“标签”。

 

本文来源于我看到的一篇文档,具体来源不可考,我觉得对于引用讲的还是比较清楚的。引用以及可变对象不可变对象,在Python中时比较重要的,因为在接下来的学习中,都会有意无意地用到。

 

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Python传值与传址

传值与传址的区别 传值就是传入一个参数的值,传址就是传入一个参数的地址,也就是内存的地址(相当于指针)。他们的区别是如果函数里面对传入的参数重新赋值,函数外的全局变量是否相应改变...

osc_adpilc97
2018/07/10
2
0
python中给函数传参是传值还是传引用

首先还是应该科普下函数参数传递机制,传值和传引用是什么意思?    函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递...

osc_6093h42a
2019/09/10
3
0
python的值传递与引用传递

首先还是应该科普下函数参数传递机制,传值和传引用是什么意思?    函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递...

osc_ndbtoukb
2019/12/10
2
0
python函数传参是传值还是传引用?

python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的...

小家雀
2019/01/18
0
0
探索 Python 之 变量、类型和引用

在探索到 Python 函数的参数传递的时候,我不禁赞叹 Python 灵活的参数设计,但慢慢的,开始迷惑与传递参数的修改和返回。 众所周知,在 C++ 中传递参数分为传值和传引用两种,但 Python 没有...

charlesdong1989
2012/03/29
785
2

没有更多内容

加载失败,请刷新页面

加载更多

macz技巧分享—macOS高端使用技巧

Macos 的占有量不如 Windows,两者之间当操作方式也有很大的不同,当很多人熱悉 Windows 的操作之后,再接触 macos,觉得难上手,其实是习惯问题。如果你学习一些技巧,会觉得 macos 其实也不...

mac小叮当
今天
11
0
手把手教你如何用黑白显示器显示彩色!

来源:大数据文摘 本文约1000字,建议阅读6分钟。 本文为你介绍如何通过黑白显示器上也能显示出彩色。 原来在黑白显示器上也能显示出彩色啊!通过在监视器上覆盖拜耳滤色镜,并拼接彩色图像,...

osc_jklrr90y
今天
18
0
key-value结构排序:给定一个字符串,统计每个字符出现频率,先按value降序,再按key升序

对于key-value结构的排序 第一种:lambda表达式 第二种:函数 第三种:类对()的重载,仿函数形式 #include <iostream>#include <vector>#include <unordered_map>#include <string>#in......

osc_gwtkg2dc
今天
0
0
BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球区块链创新50强》

BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球区块链创新50强》 目录 世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球...

osc_vew1u0h0
今天
0
0
BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》(三)

BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》(三) 目录 2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》 演讲嘉宾 演讲内容 ...

osc_8o71811p
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部