Python缺省值参数之坑
最近学习编程的过程中,被一个大坑折磨了挺久。
当时我正在编写一个函数,函数的过程的参数是一个带缺省值的列表对象,该函数需要对列表进行修改,最终函数的返回值是这个修改后的列表。整个函数可以笼统成下面这样简单的函数:
修改列表函数
可以看到append_lst( )这个函数特别简单,就是将输入变量lst的尾部增加一个1,最后将其返回。最下面两行是将调用得到的列表赋值给lst_update,而后将其打印出来。
试着执行一下:
第一次调用append_lst( )
输出结果是一个只有一个元素的列表[1],没什么奇怪的,对吧?
继续调用一次append_lst( )函数,也就是继续执行一次上面jupyter_notebook中的第二个单位格,得到如下结果:
第二次调用append_lst( )
嗯?为什么结果是[1, 1],第二次执行的时候,函数append_lst( ) 一字没改,调用的方式也一个字没有改,结果理应和第一次调用显示相同才对!这次的结果居然是列表[1, 1],而不是上一次调用得到的[1]!究竟是什么力量在作祟,还是出现了某种神秘的意外?
再调用一次看看吧:
第三次调用append_lst( )
诶呦我的天,真是奇了怪了!第三次调用的结果比第二次还要长!同样的函数,三次调用三次结果都不同,这函数真是太不稳固了!小小的函数体中究竟蕴藏了什么样的秘密?
在向各路大神求助的过程中,这个坑,曾经坑倒无数学习Python的少年,绝对算得上Python的经典大坑。这道题甚至出现在很多人的面试题中。
Python面试题
破局关键:可变对象
其中有一位大神一阵见血地指出了关键:说并不是所有带有缺省值的函数都会这么不讲道理,只有列表、集和等这样的可变对象才会出现这样的情况。这需要了解Python中的一个特性:所有的变量名都是各种对象的名字,不是对象的本身,而一个对象可以有多个变量名指向它。
对于int,float,string, tuple, dict这些不可变的变量,我们用赋值语句对他们进行赋值看起来像是修改了他们的值,但是本质上只是创立了一个新的对象,并且将原来的变量名指向新创立的对象。真正的修改是针对list、set这些可变变量而言的,如下图:
修改列表
首先创立一个[1, 2, 3]列表对象,并且用my_lst1这个变量名指向它,而后用另一个变量名my_lst_2指向my_lst_1所指向的对象,这样两个变量名就都指向了同样的对象[1, 2, 3]。通过”A is B”这样的判断语句,发现两个变量的确都指向了同样的对象。而后用append( )函数修改my_lst1所指向的对象,这时候打印出my_lst1,相应的值的确发生了改变。而在此刻打印出my_lst2的值,会发现即使刚刚没有手动修改my_lst2的值,但是其结果还是跟着改变了。这个试验可以说明:列表是一个可变对象,这个对象的值修改时,所有指向这个对象的变量也会跟着修改。
根本起因
大神说,在了解了可变变量的原理后,即可以了解一开始的问题了。再看一眼一开始的函数:
append_lst( )函数
第一次调用函数时,变量lst的确被缺省值赋予了空列表,此后,根据append( )方法,lst被修改为[1],并且作为返回值传递给了lst_update。在这个时候,lst和lst_update两个变量同时指向了相同的对象[1]。在Python中对象的作用域由变量名决定,这个时候,因为指向对象[1]的变量lst_update是全局变量, 指向[1]的另一个变量lst因而也取得了全局变量的公告周期(但不是说lst就成了全局变量,由于lst并不能在函数append_lst( )之外的地方被使用),也就是说,lst这个变量并不会像普通的局部变量一样随着函数的结束而消亡。
这样第二次调用append_lst( )函数的时候,由于变量名lst仍旧指向着[1]这个对象,所以其不在会被def append_lst(lst=[ ])语句重新赋予初始值[ ]。最后函数调用后返回的结果自然也是再对象[1]的基础上再增加,变成[1, 1]。
第三次调用时,lst和append_lst共同指向了[1, 1],调用的结果也是再[1, 1]上再增加,变成成了[1, 1, 1]。
试验验证
其实要验证大神的说法,也很简单,只要要在每次调用append_lst( )函数的时候用”is”方法判断变量lst和变量lst_update能否指向同样的对象就行:
验证第一次调用
验证第二次调用
验证第三次调用
很容易看出在第一次调用的时候,因为lst被赋予了空列表,其与lst_update指向的对象不是用一个,它们的id号也不是相同的。而在第二次和第三次调用的时候,lst和lst_update的确都指向这同样的对象,从它们的id号相同也可以看出。由此可见大神的说法的确是对的。
处理办法
大神说对了,但是这个坑背后的深层次起因就是Python解决机制的问题了。我以为先不用太去深究背后深层次起因,但是认得这个坑,知道这个坑的处理办法还是必要的。
下次假如遇到这样的情况:某个函数的参数是个可变对象,函数要对这个对象进行修改。可以不需要设置返回值和参数对象相同。由于可变对象在函数内进行修改,函数外指向该对象的全局变量也会得到修改。同时可以用bool值作为函数的返回类型,告诉调用处能否修改成功,如下图所示:
一种代替原方案的模式
当然在具体的情况中,也应该想想有什么更好的方式去处理,总之下次遇到可变变量作为有缺省值的函数参数这样的坑的时候,千万不能一头跳进去。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Python缺省值参数之坑