Python为何不能用可变对象作为默认参数的值


Posted in Python onJuly 01, 2019

先来看一道题目:

>>> def func(numbers=[], num=1):
... numbers.append(num)
... return numbers
>>> func()
[1]
>>> func()
[1, 1]
>>> func()
[1, 1, 1]

我们似乎发现了一个Bug,每次用相同的方式调用函数 func() 时,返回结果竟然不一样,而且每次返回的列表在不断地变长。

>>> id(func())
4330472840
>>> id(func())
4330472840

从上面可以看出,函数的返回值其实是同一个列表对象,因为他们的id值是一样的,只不过是列表中的元素在变化。为什么会这样呢?

这要从函数的特性说起,在 Python 中,函数是第一类对象(function is the first class object),换而言之,函数也是对象,跟整数、字符串一样可以赋值给变量、当做参数传递、还可以作为返回值。函数也有自己的属性,比如函数的名字、函数的默认参数列表。

# 函数的名字
>>> func.__name__ 
'func'
# 函数的默认参数列表
>>> func.__defaults__ 
([1, 1, 1, 1, 1], 1)

def是一条可执行语句,Python 解释器执行 def 语句时,就会在内存中就创建了一个函数对象(此时,函数里面的代码逻辑并不会执行,因为还没调用嘛),在全局命名空间,有一个函数名(变量叫 func)会指向该函数对象,记住,至始至终,不管该函数调用多少次,函数对象只有一个,就是function object,不会因为调用多次而出现多个函数对象。

Python为何不能用可变对象作为默认参数的值

函数对象生成之后,它的属性:名字和默认参数列表都将初始化完成。

Python为何不能用可变对象作为默认参数的值

初始化完成时,属性 __default__ 中的第一个默认参数 numbers 指向一个空列表。

当函数第一次被调用时,就是第一次执行 func()时,开始执行函数里面的逻辑代码(此时函数不再需要初始化了),代码逻辑就是往numbers中添加一个值为1的元素

Python为何不能用可变对象作为默认参数的值

第二次调用 func(),继续往numbers中添加一个元素

Python为何不能用可变对象作为默认参数的值

第三次、四次依此类推。

所以现在你应该明白为什么调用同一个函数,返回值确每次都不一样了吧。因为他们共享的是同一个列表(numbers)对象,只是每调用一次就往该列表中增加了一个元素

如果我们显示地指定 numbers 参数,结果截然不同。

>>> func(numbers=[10, 11])
[10, 11, 1]

Python为何不能用可变对象作为默认参数的值

因为numbers被重新赋值了,它不再指向原来初始化时的那个列表了,而是指向了我们传递过去的那个新列表对象,因此返回值变成了 [10, 11, 1]

那么我们应该如何避免前面那种情况发生呢?就是不要用可变对象作为参数的默认值。

正确方式:

>>> def func(numbers=None, num=1):
... if numbers is None:
... numbers = [num]
... else:
... numbers.append(num)
... return numbers
...
>>> func()
[1]
>>> func()
[1]
>>> func()
[1]

如果调用时没有指定参数,那么调用方法时,默认参数 numbers 每次都被重新赋值了,所以,每次调用的时候numbers都将指向一个新的对象。这就是与前者的区别所在。

那么,是不是说我们永远都不应该用可变对象来作为参数的默认值了吗?并不是,既然Python有这样的语法,就一定有他的应用场景,就像 for ... else 语法一样。我们可以用可变对象来做缓存功能。

例如:计算一个数的阶乘时可以用一个可变对象的字典当作缓存值来实现缓存,缓存中保存计算好的值,第二次调用的时候就无需重复计算,直接从缓存中拿。

def factorial(num, cache={}):
if num == 0:
return 1
if num not in cache:
print('xxx')
cache[num] = factorial(num - 1) * num
return cache[num]
print(factorial(4))
print("-------")
print(factorial(4))

输出:

---第一次调用---
xxx
xxx
xxx
xxx
24
---第二次调用---
24

第二次调用的时候,直接从 cache 中拿了值,所以,你说用可变对象作为默认值是 Python 的缺陷吗?也并不是,对吧!你还是当作一种特性来使用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
web.py中调用文件夹内模板的方法
Aug 26 Python
Python中的赋值、浅拷贝、深拷贝介绍
Mar 09 Python
python中通过预先编译正则表达式提高效率
Sep 25 Python
和孩子一起学习python之变量命名规则
May 27 Python
实例讲解python中的协程
Oct 08 Python
python如何爬取网站数据并进行数据可视化
Jul 08 Python
python 字段拆分详解
Dec 17 Python
Python生成六万个随机,唯一的8位数字和数字组成的随机字符串实例
Mar 03 Python
基于python实现数组格式参数加密计算
Apr 21 Python
pycharm 添加解释器的方法步骤
Aug 31 Python
python+selenium自动化实战携带cookies模拟登陆微博
Jan 19 Python
pycharm无法安装cv2模块问题
May 20 Python
浅析Python与Mongodb数据库之间的操作方法
Jul 01 #Python
Python字典对象实现原理详解
Jul 01 #Python
Python Pandas 获取列匹配特定值的行的索引问题
Jul 01 #Python
Python动态语言与鸭子类型详解
Jul 01 #Python
详解python websocket获取实时数据的几种常见链接方式
Jul 01 #Python
使用python将mysql数据库的数据转换为json数据的方法
Jul 01 #Python
python字符串Intern机制详解
Jul 01 #Python
You might like
php初学者写及时补给skype用户充话费的小程序
2008/11/02 PHP
php 购物车的例子
2009/05/04 PHP
Php Mssql操作简单封装支持存储过程
2009/12/11 PHP
一步一步学习PHP(5) 类和对象
2010/02/16 PHP
PHP判断搜索引擎蜘蛛并自动记忆到文件的代码
2012/02/04 PHP
PHP file_get_contents函数读取远程数据超时的解决方法
2015/05/13 PHP
javascript中的if语句使用介绍
2013/11/20 Javascript
jQuery中last()方法用法实例
2015/01/06 Javascript
JS验证IP,子网掩码,网关和MAC的方法
2015/07/02 Javascript
判断输入的字符串是否是日期格式的简单方法
2016/07/11 Javascript
JS实现中国公民身份证号码有效性验证
2017/02/20 Javascript
jQuery插件HighCharts绘制2D饼图效果示例【附demo源码下载】
2017/03/21 jQuery
bootstrap table实现x-editable的行单元格编辑及解决数据Empty和支持多样式问题
2017/08/10 Javascript
jQuery实现打开网页自动弹出遮罩层或点击弹出遮罩层功能示例
2017/10/19 jQuery
vue中将html字符串转换成html后遇到的问题小结
2018/12/10 Javascript
vue动态添加路由addRoutes之不能将动态路由存入缓存的解决
2019/02/19 Javascript
vue过滤器用法实例分析
2019/03/15 Javascript
使用PreloadJS加载图片资源的基础方法详解
2020/02/03 Javascript
ES5 模拟 ES6 的 Symbol 实现私有成员功能示例
2020/05/06 Javascript
[03:02]2014DOTA2西雅图邀请赛 让队员自己告诉你DK NAVI备战情况
2014/07/08 DOTA
[00:43]TI7不朽珍藏III——幽鬼不朽展示
2017/07/15 DOTA
[01:05:30]VP vs TNC 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
[19:54]夜魇凡尔赛茶话会 第一期02:看图识人
2021/03/11 DOTA
python实现bitmap数据结构详解
2014/02/17 Python
Python中的getopt函数使用详解
2015/07/28 Python
python中实现指定时间调用函数示例代码
2017/09/08 Python
Window环境下Scrapy开发环境搭建
2018/11/18 Python
python3发送request请求及查看返回结果实例
2020/04/30 Python
python中的对数log函数表示及用法
2020/12/09 Python
阿里巴巴Oracle DBA笔试题答案-备份恢复类
2013/11/20 面试题
个人思想理论学习的自我鉴定
2013/11/30 职场文书
行政部总经理岗位职责
2014/01/04 职场文书
向女朋友道歉的话
2015/01/20 职场文书
2015年酒店工作总结
2015/04/28 职场文书
感恩教师主题班会
2015/08/12 职场文书
安全教育培训心得体会
2016/01/15 职场文书