Python3中函数参数传递方式实例详解


Posted in Python onMay 05, 2019

本文实例讲述了Python3中函数参数传递方式。分享给大家供大家参考,具体如下:

之前在看北理工嵩天等老师的python3的课程,在第五周中老师讲到了函数的调用传递。老师讲了这样一个例子

#处理多个银行账户的余额信息
def addInterest(balances, rate):
  for i in range(len(balances)):
    balances[i] = balances[i] * (1+rate)
def test():
  amounts = [1000, 105, 3500, 739]
  rate = 0.05
  addInterest(amounts, rate)
  print(amounts)
test()

在这个例子中可以看到为了处理多个银行账户设置了amounts这个列表,老师的原话是这样的:

“在test函数中一开始设置了amounts为4个值的列表,然后将amounts作为第一个参数传递给函数addInterest,并调用执行该函数,最后打印输出amounts的结果,运行结果如下:

[1050.0,110.25,3675.0,775.95]


然后礼欣老师得出结论,以下为原话

“在这个例子中可以看到,amounts变量的值好像是被修改了,但是函数是不能改变变量本身即amounts的”

接下来是分析下函数的执行过程,过程如下图

Python3中函数参数传递方式实例详解

分析原话如下

“接下来分析下整个执行过程,查看amounts输出结果是如何发生变化的。首先函数test的前两条语句建立了两个变量amounts和rate,然后控制传递给addinterest,这里amounts是一个包含4个整数类型值的列表对象,以实参的形式传递给函数addinterest形参balances,下一步执行函数addinterest,从0到length-1范围执行循环,并更新balances的值。”

重点来了:原话如下

“图中旧值 [1000, 105, 3500, 739]
并没有改变,只是Python又创建了一组新值[1050.0,110.25,3675.0,775.95]
,并且使列表对象指向该组新值,而旧值会在Python的垃圾数据回收的时候被清除掉③,从图中我们可以清楚的看出,为什么包含列表参数的程序addinterest修改了列表的值?但程序addinterest结束时存储在amounts中的是新balances的值,实际上变量amounts从来没有被改变过。”
“它(amounts,作者注)仍然指向的是调用addinterest函数之前的同一个列表,只是当控制返回到调用函数中时,列表呈现了被修改的状态”②

最后是得出结论,原话如下:

“通过上述过程我们可以了解到:Python的参数是通过值来传递的。但是如果变量是可变对象,比如是列表或者是图形对象,返回到调用程序后,该对象会呈现出被修改的状态。”

^_^
注:课程原始视频部分结束。

看了老师的这段讲解之后产生了很多疑问:在前面(①处)讲的amounts是不能被修改的,但是在(②处)又说列表呈现了被修改的状态,这不是自相矛盾吗?在(③)处讲列表创建了新值并且使列表指向了新值,这里不就是说amounts发生了改变吗?怎么能说没变呢?最后结论也是列表呈现出了被修改的状态。这个结论云山雾绕,看得人似懂非懂。

那在Python3中参数变量是列表,在调用函数完返回后到底是被修改了还是没被修改呢?

为了弄清这个问题,我做了一个实验,id()可以查看变量在内存中的地址,这个值相当于一个对象的“身份证”。

# 处理多个银行账户的余额信息
def addInterest(balances, rates):
print()
print("第二处", id(balances))
  for i in range(len(balances)):
    balances[i]= balances[i]*(1+rates)
    print()
    print("第三处",id(balances))
def test():
  amounts = [1000,105,3500,739]
  print()
  print("第一处",id(amounts))
  rate = 0.05
  addInterest(amounts, rate)
  print()
  print(amounts)
  print()
  print("第四处",id(amounts))
test()

输出结果:

第一处 41203656

第二处 41203656

第三处 41203656

第三处 41203656

第三处 41203656

第三处 41203656

[1050.0, 110.25, 3675.0, 775.95]

第四处 41203656

在这个实验中可以清楚的看到,amounts这个对象的身份证号码在整个程序运行过程中从未变过,而非视频中老师讲的创建了新的列表对象。所以amounts作为一个列表对象在程序运行过程中是被直接修改了,是的就是直接被修改了,而非指向新balances的值。为什么可以得出这一结论?我们可以看下第一、三处的id,在未进入函数之前id是41203656(第一处),进入函数之后对象id仍然未变,函数运行完返回之后对象id仍然未变!

所以结论应该这样写会比较清楚:

改变参数值值的函数:
实参传给形参时,python的参数是通过值来传递的;
如果变量是可变对象(如列表或者图形对象),该对象会在函数中会被直接修改,返回到调用程序后也是被修改后的状态。

那是不是Python3中函数都是像这种传递方式呢?我们对课程视频中的另一个例子做一个简单的修改。

# 计算单个银行账户余额
def addinterest(balance, rate):
  print("第二处", id(balance))
  newBalance = balance * (1 + rate)
  print()
  print("第三处", id(balance))
  print()
  print("第四处", id(newBalance))
  return newBalance
def main():
  amount = 1000
  print("第一处", id(amount))
  print()
  rate = 0.05
  amount = addinterest(amount, rate)
  print()
  print("第五处", id(amount))
  print()
  print(amount)
  print("第六处", id(amount))
main()

运行结果如下:

第一处 33533648

第二处 33533648

第三处 33533648

第四处 33563344

第五处 33563344

1050.0
第六处 33563344

不是说好的直接修改的吗?怎么身份证又变了?其实这里的对象amount是个常数,即为不可变对象,当在函数中要对对象进行处理时,由于对象不可变,只能新建一个新对象,然后return出新的对象了。

这个也就是目前网络上大部分博客的结论:

1、不可变对象作为函数参数,Python通过值传递;
2、 可变对象作为函数参数,Python通过引用传递。

注:Python中,数值类型(int和float)、字符串str、元组tuple都是不可变类型。而列表list、字典dict、集合set是可变类型。
(但是也有博客把这两个结论搞反了)

但是也有博客提出了一个类似这样的例子

def change(val):
  val = val + [10]
nums = [0, 1]
change(nums)
print(nums)

输出结果为

[0, 1]

其实这里是写的不严谨,不能直接用加号添加列表元素
可以改为这样

def change(val):
  newval = [10]
  val= val + newval
nums = [0, 1]
change(nums)
print(nums)

但是输出结果还是

[0, 1]

难道上面的结论不对吗?

其实这里要补充另外一种情况:对于可变对象作为函数参数,且参数不指向其他对象时,相当于引用传递;否则,若参数指向其他对象,则对参数变量的操作并不影响原变量的对象值

函数里的参数变量val指向了与nums不同的内存变量,所以函数里的参数变量val不影响原变量nums的值

Python3中函数参数传递方式实例详解

**这也是因为python的特性” 变量无类型,对象有类型 “。
变量是对内存空间的引用,当参数变量和原变量指向不同的内存空间时,操作互不影响。**

用下面这个看下

def change(val):
  newval = [10]
  print("第二处",id(val))
  val = val + newval
  print("第三处",id(val))
nums = [0, 1]
print("第一处",id(nums))
change(nums)
print("第四处",id(nums))
print(nums)

运行结果如下:

第一处 39695944
第二处 39695944
第三处 39710024
第四处 39695944
[0, 1]

可以看到第一处的nums和第二处的val的内存地址完全一样,然后执行到第三处时,由于函数内VAL重新指向了别的内存变量,所以内存地址不同。但是最后结果要输出变量nums,即第一处第二处内存地址的值,所以和第三处的val就没关系了。其实这里的val是没有返回值的。

想要直接在列表中添加元素可以写成这样:

def change(val):
  val.append(10)
nums = [0, 1]
change(nums)
print(nums)

输出结果是

[0, 1, 10]

关于变量无类型,对象有类型可以这样理解:只有放在内存空间中的对象(也就是数据)才有类型,而变量是没有类型的。

如果还是不明白可以做这样一种比喻:变量就好比钓鱼者,湖水就好像内存,里面有各种各样的鱼,它们就是对象。钓鱼者(变量)的任务就是用某种方式把自己和鱼(对象)通过鱼线连接起来。那么,鱼(对象)是有类型的,有鲢鱼、鲫鱼、带鱼。钓鱼者(变量)没有类型,他钓到不同类型的鱼(对象)。

用钓鱼的比喻解释下上面的例子

def change(val):
  newval = [10]
  val= val + newval
nums = [0, 1]
change(nums)
print(nums)

1、钓鱼人已经钓了一桶鱼用nums桶装着,nums桶可以装很多鱼。

2、现在提着这个nums桶继续在湖里钓鱼,这时候nums桶暂时叫做装鱼桶val,突然钓鱼人钓了一条大鱼,发现装鱼桶val装不下,于是钓鱼人又在渔具店买了另一个大的装鱼桶VAL,把大鱼和之前的鱼一块装了。
钓鱼活动结束。

3、最后要看看那个叫nums的桶有哪些鱼,这时候当然只能看之前的情况。

即这个结论:对于可变对象作为函数参数,且参数不指向其他对象时,相当于引用传递;否则,若参数指向其他对象,则对参数变量的操作并不影响原变量的对象值。

同样的针对其他两个结论,也可以用这个比喻解释:

def change( val):
  newval = val + 10
  return newval
num = 1
num = change(num)
print(num)

1、钓鱼人手上的东西num是个小蚯蚓。

2、钓鱼人拿着num去湖边钓鱼,小蚯蚓被大鱼吃了,钓鱼人钓到了一条大鱼,钓鱼人拿着鱼回家。
钓鱼活动结束。

3、问钓鱼人手上现在拿着什么东西num?当然是一条大鱼。

def change(val):
  val.append(10)
nums = [0, 1]
change(nums)
print(nums)

1、钓鱼人提着一个叫nums的桶,桶里装着2条鱼

2、钓鱼人来到湖边钓鱼,此时桶暂时叫装鱼桶val,钓鱼人钓到了一条鱼放进装鱼桶val。
钓鱼活动结束。

3、看看钓鱼人桶里的有几条鱼。

总结来说:

**对于不可变对象作为函数参数,相当于C系语言的值传递;
对于可变对象作为函数参数,且参数不指向其他对象时,相当于C系语言的引用传递。
对于可变对象作为函数参数,参数指向其他对象,对参数变量的操作不影响原变量的值。**

关于Python相关内容感兴趣的读者可查看本站专题:《Python函数使用技巧总结》、《Python面向对象程序设计入门与进阶教程》、《Python数据结构与算法教程》、《Python字符串操作技巧汇总》、《Python编码操作技巧总结》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python标准日志模块logging的使用方法
Nov 01 Python
Python学习笔记之if语句的使用示例
Oct 23 Python
微信跳一跳python自动代码解读1.0
Jan 12 Python
Django开发中复选框用法示例
Mar 20 Python
Python3.6中Twisted模块安装的问题与解决
Apr 15 Python
PyQt4实时显示文本内容GUI的示例
Jun 14 Python
简单了解python反射机制的一些知识
Jul 13 Python
python实现美团订单推送到测试环境,提供便利操作示例
Aug 09 Python
探秘TensorFlow 和 NumPy 的 Broadcasting 机制
Mar 13 Python
Pandas缺失值2种处理方式代码实例
Jun 13 Python
python如何安装下载后的模块
Jul 03 Python
python3中确保枚举值代码分析
Dec 02 Python
python制作填词游戏步骤详解
May 05 #Python
python开发游戏的前期准备
May 05 #Python
Python实现多态、协议和鸭子类型的代码详解
May 05 #Python
用uWSGI和Nginx部署Flask项目的方法示例
May 05 #Python
基于python实现高速视频传输程序
May 05 #Python
Python远程视频监控程序的实例代码
May 05 #Python
Python统计一个字符串中每个字符出现了多少次的方法【字符串转换为列表再统计】
May 05 #Python
You might like
在“咖啡之国”感受咖啡文化
2021/03/03 咖啡文化
使用PHP Socket 编程模拟Http post和get请求
2014/11/25 PHP
PHP合并数组函数array_merge用法分析
2017/02/17 PHP
PHP中如何使用Redis接管文件存储Session详解
2018/11/28 PHP
javascript 类方法定义还是有点区别
2009/04/15 Javascript
9个javascript语法高亮插件 推荐
2009/07/18 Javascript
JSDoc 介绍使用规范JsDoc的使用介绍
2011/02/12 Javascript
早该知道的7个JavaScript技巧
2013/03/27 Javascript
jQuery实现的图片分组切换焦点图插件
2015/01/06 Javascript
jquery+php实现滚动的数字特效
2015/11/29 Javascript
JQuery中Ajax()的data参数类型实例分析
2015/12/15 Javascript
通过node-mysql搭建Windows+Node.js+MySQL环境的教程
2016/03/01 Javascript
JS组件Bootstrap dropdown组件扩展hover事件
2016/04/17 Javascript
JSON 必知必会 观后记
2016/10/27 Javascript
JavaScript浏览器对象模型BOM(BrowserObjectModel)实例详解
2016/11/29 Javascript
微信小程序中使用Promise进行异步流程处理的实例详解
2017/08/17 Javascript
JS使用正则表达式提交页面验证的代码
2019/10/16 Javascript
NodeJS http模块用法示例【创建web服务器/客户端】
2019/11/05 NodeJs
python获取豆瓣电影简介代码分享
2014/01/16 Python
python网络编程之读取网站根目录实例
2014/09/30 Python
Python 备份程序代码实现
2017/03/06 Python
Linux RedHat下安装Python2.7开发环境
2017/05/20 Python
Windows下将Python文件打包成.EXE可执行文件的方法
2018/08/03 Python
python实现扫描ip地址的小程序
2019/04/16 Python
python调用webservice接口的实现
2019/07/12 Python
python 实现弹球游戏的示例代码
2020/11/17 Python
HTML5 canvas实现移动端上传头像拖拽裁剪效果
2016/03/14 HTML / CSS
美国葡萄酒网上商店:Martha Stewart Wine Co.
2019/03/17 全球购物
美国正宗设计师眼镜在线零售商:EYEZZ
2019/03/23 全球购物
自荐信的两点禁忌
2013/10/30 职场文书
参观监狱心得体会
2014/01/02 职场文书
母亲节演讲稿范文
2014/01/02 职场文书
酒店销售主管岗位职责
2014/01/04 职场文书
个人授权委托书范本
2014/09/14 职场文书
导游词之江西赣州
2019/10/15 职场文书
对象析构函数__del__在Python中何时使用
2022/03/22 Python