当前位置:脚本大全 > > 正文

python3常用内建函数(Python3中函数参数传递方式实例详解)

时间:2021-10-05 00:39:40类别:脚本大全

python3常用内建函数

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

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

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • #处理多个银行账户的余额信息
  • 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常用内建函数(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()可以查看变量在内存中的地址,这个值相当于一个对象的“身份证”。

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • # 处理多个银行账户的余额信息
  • 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中函数都是像这种传递方式呢?我们对课程视频中的另一个例子做一个简单的修改。

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • # 计算单个银行账户余额
  • 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是可变类型。
    (但是也有博客把这两个结论搞反了)

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • def change(val):
  •   val = val + [10]
  • nums = [0, 1]
  • change(nums)
  • print(nums)
  • 输出结果为

    [0, 1]

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • def change(val):
  •   newval = [10]
  •   val= val + newval
  • nums = [0, 1]
  • change(nums)
  • print(nums)
  • 但是输出结果还是

    [0, 1]

    难道上面的结论不对吗?

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

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

    python3常用内建函数(Python3中函数参数传递方式实例详解)

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

    用下面这个看下

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 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是没有返回值的。

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • def change(val):
  •   val.append(10)
  • nums = [0, 1]
  • change(nums)
  • print(nums)
  • 输出结果是

    [0, 1, 10]

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

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

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 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的桶有哪些鱼,这时候当然只能看之前的情况。

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

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • def change( val):
  •   newval = val + 10
  •   return newval
  • num = 1
  • num = change(num)
  • print(num)
  • 1、钓鱼人手上的东西num是个小蚯蚓。

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

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

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • def change(val):
  •   val.append(10)
  • nums = [0, 1]
  • change(nums)
  • print(nums)
  • 1、钓鱼人提着一个叫nums的桶,桶里装着2条鱼

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

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

    总结来说:

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

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

    原文链接:https://blog.csdn.net/wps20001990/article/details/79394502

    上一篇下一篇

    猜您喜欢

    热门推荐