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装饰器
Jun 15 Python
如何高效使用Python字典的方法详解
Aug 31 Python
python3+PyQt5实现文档打印功能
Apr 24 Python
Python Learning 列表的更多操作及示例代码
Aug 22 Python
Python实现点阵字体读取与转换的方法
Jan 29 Python
TensorFlow实现自定义Op方式
Feb 04 Python
keras模型保存为tensorflow的二进制模型方式
May 25 Python
PyQt5实现画布小程序
May 30 Python
Pytorch 扩展Tensor维度、压缩Tensor维度的方法
Sep 09 Python
Python+OpenCV图像处理——打印图片属性、设置存储路径、调用摄像头
Oct 22 Python
python中二分查找法的实现方法
Dec 06 Python
python快速安装OpenCV的步骤记录
Feb 22 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
ThinkPHP写数组插入与获取最新插入数据ID实例
2014/11/03 PHP
PHP微信开发之二维码生成类
2015/06/26 PHP
Javascript 更新 JavaScript 数组的 uniq 方法
2008/01/23 Javascript
javascript实现在网页中运行本地程序的方法
2016/02/03 Javascript
Bootstrap编写一个在当前网页弹出可关闭的对话框 非弹窗
2016/06/30 Javascript
利用js+css+html实现固定table的列头不动
2016/12/08 Javascript
jquery实现输入框实时输入触发事件代码
2016/12/21 Javascript
vue+axios实现登录拦截的实例代码
2017/05/22 Javascript
妙用Angularjs实现表格按指定列排序
2017/06/23 Javascript
JS中把函数作为另一函数的参数传递方法(总结)
2017/06/28 Javascript
AngularJS中下拉框的高级用法示例
2017/10/11 Javascript
JS实现点击复选框变更DIV显示状态的示例代码
2017/12/18 Javascript
浅谈vue-router 路由传参的方法
2017/12/27 Javascript
vue中使用cookies和crypto-js实现记住密码和加密的方法
2018/10/18 Javascript
Vue中ref和$refs的介绍以及使用方法示例
2021/01/11 Vue.js
Vue实现简单计算器
2021/01/20 Vue.js
python 3.6 tkinter+urllib+json实现火车车次信息查询功能
2017/12/20 Python
详解Django+Uwsgi+Nginx的生产环境部署
2018/06/25 Python
实例讲解python中的序列化知识点
2018/10/08 Python
python多进程控制学习小结
2018/10/31 Python
Python数据预处理之数据规范化(归一化)示例
2019/01/08 Python
PyQt5 加载图片和文本文件的实例
2019/06/14 Python
python实现接口并发测试脚本
2019/06/25 Python
Python中的 sort 和 sorted的用法与区别
2019/08/10 Python
python3实现高效的端口扫描
2019/08/31 Python
Python编写一个验证码图片数据标注GUI程序附源码
2019/12/09 Python
Python开发之基于模板匹配的信用卡数字识别功能
2020/01/13 Python
python匿名函数lambda原理及实例解析
2020/02/07 Python
python3 使用traceback定位异常实例
2020/03/09 Python
Django 如何使用日期时间选择器规范用户的时间输入示例代码详解
2020/05/22 Python
Pat McGrath Labs官网:世界上最有影响力的化妆师推出的彩妆品牌
2018/01/07 全球购物
学校后勤岗位职责
2014/02/19 职场文书
校庆接待方案
2014/03/18 职场文书
毕业论文评语大全
2014/04/29 职场文书
2014年秋季开学寄语
2014/08/02 职场文书
务虚会发言材料
2014/12/25 职场文书