python 默认参数问题的陷阱


Posted in Python onFebruary 29, 2016

python 里面一个常见的陷阱就是函数的默认参数问题。如下:

def func(mylist = []):
  mylist.append(1)
  return mylist

以下的执行结果如下:

print func()
print func()
print func()
print func(['a'])
print func()

结果如下:

[1]
[1, 1]
[1, 1, 1]
['a', 1]
[1, 1, 1, 1]

如此结果, 前面三个可以看出 如果没有指定参数的话, 每次调用函数时候, 调用的mylist 是同一个对象。这是因为函数的默认参数,是在代码编译成PyCodeObject的时候, 就已经创建了对象指针,并且存在该函数的func_default内。 以后在代码运行,调用函数的时候,如果没有指定参数的话, 每次调用的话, 该参数变量都是代码编译阶段的变量指针所指定的对象。

print func.func_default

此时结果就是:

([1, 1, 1, 1], )
默认参数分为两种情况:

默认参数值是不可变对象
此时函数的 func_default 一直指向该不变对象, 如果函数内部修改了该变量, 那么该默认参数会指向一个新的不可变对象.
不过func_default 不变。 而每次调用函数都是读取func_default, 因此每次执行都一样。

In [30]: def func2(var = 1):
  ....:   var += 1
  ....:   return var
  ....: 

In [31]: func2()
Out[31]: 2

In [32]: func2()
Out[32]: 2

In [34]: func2.func_defaults
Out[34]: (1,)

默认参数是可变对象,比如 list, dict, class等
这种情况下,如果在函数内修改了指针所指的对象(并未创建新的对象), 那么 func_default 就会改变。这正是开始的mylist发生变化的原因。看下面的例子,:

In [35]: def func(mylist = []):
  ....:   mylist = []  #这里 创建了新的对象,
       mylist.append(1)
       return mylist

In [44]: func()
Out[44]: [1]

In [45]: func.func_defaults
Out[45]: ([],)

由于创建了对象, mylist 只是作为一个 新建对象的别名存在, 后面在修改已经与 func_default 无关了。 
默认参数的一个应用

先看下面的一个经典的例子:

def outer():
  res = []
  for i in range(4):
    def inner(j):
      return j * i
    res.append(inner)
  return res

print [m(2) for m in outer()]

#简略版本:

def multipliers():
  return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]

结果是 [6, 6, 6, 6] , 而不是 [0, 2, 4, 6], 原因就是闭包的延迟绑定。另外函数绑定的是变量而不是绑定数值。当循环结束了,i的值已经是3, 此时结果都是6. 一个解决方法便是,使用默认参数绑定数值。如下改动:

def outer():
  res = []
  for i in range(4):
    def inner(j, i = i):
      return j * i
    res.append(inner)
  return res

print [m(2) for m in outer()]

#简略版本:

def multipliers():
  return [lambda x, i = i : i * x for i in range(4)]
print [m(2) for m in multipliers()]

这样的话, 利用默认参数在代码编译的时候,便把参数写到函数的func_default中, 就可以绑定0,1,2,3了。结果自然就是

[0, 2, 4, 6]
这就是默认参数的一个应用。

上述还有一个生成器修改的方式

def multipliers():
  return (lambda x, i = i : i * x for i in range(4)) #修改成生成器
print [m(2) for m in multipliers()]
Python 相关文章推荐
Python中实现远程调用(RPC、RMI)简单例子
Apr 28 Python
python将字符串转换成数组的方法
Apr 29 Python
Python中强大的命令行库click入门教程
Dec 26 Python
python检测空间储存剩余大小和指定文件夹内存占用的实例
Jun 11 Python
python 批量修改/替换数据的实例
Jul 25 Python
python 生成图形验证码的方法示例
Nov 11 Python
python绘制漏斗图步骤详解
Mar 04 Python
OpenCV模板匹配matchTemplate的实现
Oct 18 Python
python3爬虫中引用Queue的实例讲解
Nov 24 Python
Pandas的数据过滤实现
Jan 15 Python
Python数据类型最全知识总结
May 31 Python
Python机器学习之基于Pytorch实现猫狗分类
Jun 08 Python
简要讲解Python编程中线程的创建与锁的使用
Feb 28 #Python
Python中time模块和datetime模块的用法示例
Feb 28 #Python
python 写的一个爬虫程序源码
Feb 28 #Python
Python基础语法(Python基础知识点)
Feb 28 #Python
python中map()与zip()操作方法
Feb 27 #Python
python中input()与raw_input()的区别分析
Feb 27 #Python
python PIL模块与随机生成中文验证码
Feb 27 #Python
You might like
星际争霸 Starcraft 发展史
2020/03/14 星际争霸
如何使用Linux的Crontab定时执行PHP脚本的方法
2011/12/19 PHP
PHP和JavaScrip分别获取关联数组的键值示例代码
2013/09/16 PHP
Thinkphp中的curd应用实用要点
2015/01/04 PHP
javascript+php实现根据用户时区显示当地时间的方法
2015/03/11 PHP
TP5框架安全机制实例分析
2020/04/05 PHP
深入认识javascript中的eval函数
2009/11/02 Javascript
我的javascript 函数链之演变
2011/04/07 Javascript
JS实现的生成随机数的4个函数分享
2015/02/11 Javascript
jQuery的内容过滤选择器学习教程
2016/04/18 Javascript
onclick和onblur冲突问题的快速解决方法
2016/04/28 Javascript
node.js程序作为服务并在windows下开机自启动(用forever)
2017/03/29 Javascript
使用FileReader API创建Vue文件阅读器组件
2018/04/03 Javascript
javascript标准库(js的标准内置对象)总结
2018/05/26 Javascript
Element输入框带历史查询记录的实现示例
2019/01/15 Javascript
vue模仿网易云音乐的单页面应用
2019/04/24 Javascript
[02:41]DOTA2英雄基础教程 冥魂大帝
2014/01/16 DOTA
python通过getopt模块如何获取执行的命令参数详解
2017/12/29 Python
解决python中 f.write写入中文出错的问题
2018/10/31 Python
Python3 main函数使用sys.argv传入多个参数的实现
2019/12/25 Python
非常漂亮的CSS3百叶窗焦点图动画
2016/02/24 HTML / CSS
美国专业级皮肤病和spa品质护肤品的高级零售网站:SkinCareRx
2017/02/06 全球购物
日本高岛屋百货购物网站:TAKASHIMAYA
2019/03/24 全球购物
德国户外商店:eXXpozed
2020/07/25 全球购物
琳达·法罗眼镜英国官网:Linda Farrow英国
2021/01/19 全球购物
电子商务专业推荐信范文
2013/12/02 职场文书
班级活动策划书
2014/02/06 职场文书
有关九一八事变的演讲稿
2014/09/14 职场文书
2014党员批评和自我批评思想汇报
2014/09/21 职场文书
高中生社会实践心得体会
2016/01/14 职场文书
2016党员学习《反对自由主义》心得体会
2016/01/22 职场文书
写给医护人员的一封感谢信
2019/09/16 职场文书
《飘》英文读后感五篇
2019/10/11 职场文书
详解PHP用mb_string处理windows中文字符
2021/05/26 PHP
启动Tomcat时出现大量乱码的解决方法
2021/06/21 Java/Android
总结Java对象被序列化的两种方法
2021/06/30 Java/Android