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中装饰器级连的使用方法示例
Sep 29 Python
python通过getopt模块如何获取执行的命令参数详解
Dec 29 Python
Python实现的径向基(RBF)神经网络示例
Feb 06 Python
详解PyTorch批训练及优化器比较
Apr 28 Python
基于windows下pip安装python模块时报错总结
Jun 12 Python
Python中分支语句与循环语句实例详解
Sep 13 Python
Python控制Firefox方法总结
Jun 03 Python
python算法与数据结构之冒泡排序实例详解
Jun 22 Python
解决python明明pip安装成功却找不到包的问题
Aug 28 Python
Python 多线程,threading模块,创建子线程的两种方式示例
Sep 29 Python
Python模拟键盘输入自动登录TGP
Nov 27 Python
Selenium+BeautifulSoup+json获取Script标签内的json数据
Dec 07 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定时执行计划任务的多种方法小结
2011/12/19 PHP
PHP图片处理之使用imagecopy函数添加图片水印实例
2014/11/19 PHP
WordPress免插件实现面包屑导航的示例代码
2020/08/20 PHP
JavaScript DOM学习第一章 W3C DOM简介
2010/02/19 Javascript
根据出生日期自动取得星座的js代码
2010/07/20 Javascript
jQuery-Tools-overlay 使用介绍
2012/07/14 Javascript
jQuery简单图表peity.js使用示例
2014/05/02 Javascript
简介JavaScript中的push()方法的使用
2015/06/09 Javascript
javascript实现查找数组中最大值方法汇总
2016/02/13 Javascript
JS实现兼容各种浏览器的获取选择文本的方法【测试可用】
2016/06/21 Javascript
jQuery mobile的header和footer在点击屏幕的时候消失的解决办法
2016/07/01 Javascript
gulp-uglify 与gulp.watch()配合使用时报错(重复压缩问题)
2016/08/24 Javascript
jQuery取得元素标签名称小结(附代码)
2017/08/16 jQuery
vue.js组件vue-waterfall-easy实现瀑布流效果
2017/08/22 Javascript
轻松解决JavaScript定时器越走越快的问题
2019/05/13 Javascript
vue element-ui之怎么封装一个自己的组件的详解
2019/05/20 Javascript
vue-property-decorator用法详解
2019/12/12 Javascript
一看就会的vuex实现登录验证(附案例)
2020/01/09 Javascript
python多线程http下载实现示例
2013/12/30 Python
Python实现将数据框数据写入mongodb及mysql数据库的方法
2018/04/02 Python
python实现的汉诺塔算法示例
2019/10/23 Python
pygame实现五子棋游戏
2019/10/29 Python
python实现计算器功能
2019/10/31 Python
Python性能分析工具Profile使用实例
2019/11/19 Python
python运用pygame库实现双人弹球小游戏
2019/11/25 Python
python 连续不等式语法糖实例
2020/04/15 Python
基于Keras中Conv1D和Conv2D的区别说明
2020/06/19 Python
python+opencv3.4.0 实现HOG+SVM行人检测的示例代码
2021/01/28 Python
办公自动化专业大学生职业规划书
2014/03/06 职场文书
银行主办会计岗位职责
2014/08/13 职场文书
党员批评与自我批评思想汇报
2014/10/08 职场文书
领导个人查摆剖析材料
2014/10/29 职场文书
高校自主招生教师推荐信
2015/03/23 职场文书
乡镇保密工作承诺书
2015/05/04 职场文书
Java常用工具类汇总 附示例代码
2021/06/26 Java/Android
Go语言基础知识点介绍
2021/07/04 Golang