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 相关文章推荐
使用go和python递归删除.ds store文件的方法
Jan 22 Python
用Python输出一个杨辉三角的例子
Jun 13 Python
Python合并字符串的3种方法
May 21 Python
Python批量修改文本文件内容的方法
Apr 29 Python
Python数据分析之如何利用pandas查询数据示例代码
Sep 01 Python
浅谈python中的占位符
Nov 09 Python
django DRF图片路径问题的解决方法
Sep 10 Python
详解python 爬取12306验证码
May 10 Python
python+opencv实现摄像头调用的方法
Jun 22 Python
django之静态文件 django 2.0 在网页中显示图片的例子
Jul 28 Python
Python3 JSON编码解码方法详解
Sep 06 Python
PyCharm中Matplotlib绘图不能显示UI效果的问题解决
Mar 12 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
PHP安装全攻略:APACHE
2006/10/09 PHP
php单例模式实现(对象只被创建一次)
2012/12/05 PHP
完美解决在ThinkPHP控制器中命名空间的问题
2017/05/05 PHP
Yii 2.0如何使用页面缓存方法示例
2017/05/23 PHP
总结PHP中初始化空数组的最佳方法
2019/02/13 PHP
自己写的兼容ie和ff的在线文本编辑器类似ewebeditor
2012/12/12 Javascript
js 采用delete实现继承示例代码
2014/05/20 Javascript
解析javascript图片懒加载与预加载的分析总结
2016/10/27 Javascript
JavaScript模块化之使用requireJS按需加载
2017/04/12 Javascript
vue使用Axios做ajax请求详解
2017/06/07 Javascript
jQuery实现动态添加节点与遍历节点功能示例
2017/11/09 jQuery
移动端自适应flexible.js的使用方法(不用三大框架,仅写一个单html页面使用)推荐
2019/04/02 Javascript
基于JS实现前端压缩上传图片的实例代码
2019/05/14 Javascript
JS数组方法shift()、unshift()用法实例分析
2020/01/18 Javascript
JS严格模式原理与用法实例分析
2020/04/27 Javascript
JS变量提升及函数提升实例解析
2020/09/03 Javascript
利用python发送和接收邮件
2016/09/27 Python
对python For 循环的三种遍历方式解析
2019/02/01 Python
python 中的列表生成式、生成器表达式、模块导入
2019/06/19 Python
Python pip替换为阿里源的方法步骤
2019/07/02 Python
python 读取数据库并绘图的实例
2019/12/03 Python
如何让PyQt5中QWebEngineView与JavaScript交互
2020/10/21 Python
Sublime Text3最新激活注册码分享适用2020最新版 亲测可用
2020/11/12 Python
美国最大的团购网站:Groupon
2016/07/23 全球购物
德国高性价比网上药店:medpex
2017/07/09 全球购物
如何写自我评价?自我评价写什么好?
2014/03/14 职场文书
低碳环保口号
2014/06/12 职场文书
2014领导班子四风问题查摆思想汇报
2014/09/13 职场文书
毕业生对母校寄语
2015/02/26 职场文书
写给老婆的保证书
2015/02/27 职场文书
拖欠货款起诉状
2015/05/20 职场文书
《丑小鸭》教学反思
2016/02/19 职场文书
2019银行竞聘书
2019/06/21 职场文书
2019年12月24日平安夜祝福语集锦
2019/12/24 职场文书
python 实现德洛内三角剖分的操作
2021/04/22 Python
Java 数据结构七大排序使用分析
2022/04/02 Java/Android