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使用Socket(Https)Post登录百度的实现代码
May 18 Python
python 基础学习第二弹 类属性和实例属性
Aug 27 Python
Python对字符串实现去重操作的方法示例
Aug 11 Python
Python实现删除时保留特定文件夹和文件的示例
Apr 27 Python
Django自定义用户登录认证示例代码
Jun 30 Python
安装python及pycharm的教程图解
Oct 10 Python
python 实现目录复制的三种小结
Dec 04 Python
基于pytorch的lstm参数使用详解
Jan 14 Python
Python并发concurrent.futures和asyncio实例
May 04 Python
10行Python代码实现Web自动化管控的示例代码
Aug 14 Python
详解Java中一维、二维数组在内存中的结构
Feb 11 Python
matplotlib之属性组合包(cycler)的使用
Feb 24 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
关于Appserv无法打开localhost问题的解决方法
2009/10/16 PHP
php实现文件编码批量转换
2014/03/10 PHP
PHP对称加密算法(DES/AES)类的实现代码
2017/11/14 PHP
PHP学习记录之数组函数
2018/06/01 PHP
laravel在中间件内生成参数并且传递到控制器中的2种姿势
2019/10/15 PHP
让iframe自适应高度(支持XHTML,支持FF)
2007/07/24 Javascript
js 实现无干扰阴影效果 简单好用(附文件下载)
2009/12/27 Javascript
JavaScript 异步方法队列链实现代码分析
2010/06/05 Javascript
myFocus slide3D v1.1.0 使用方法与下载
2011/01/12 Javascript
jQuery调用WebService的实现代码
2011/06/19 Javascript
浏览器打开层自动缓慢展开收缩实例代码
2013/07/04 Javascript
jquery 获取dom固定元素 添加样式的简单实例
2014/02/04 Javascript
超链接的禁用属性Disabled使用示例
2014/07/31 Javascript
js插件YprogressBar实现漂亮的进度条效果
2015/04/20 Javascript
jQuery实现按钮的点击 全选/反选 单选框/复选框 文本框 表单验证
2015/06/25 Javascript
jQuery实现标题有打字效果的焦点图代码
2015/11/16 Javascript
AngularJS 基础ng-class-even指令用法
2016/08/01 Javascript
react.js 翻页插件实例代码
2017/01/19 Javascript
ES6正则的扩展实例详解
2017/04/25 Javascript
详解AngularJS controller调用factory
2017/05/19 Javascript
BootStrap 页签切换失效的解决方法
2017/08/17 Javascript
详解wow.js中各种特效对应的类名
2017/09/13 Javascript
switchery按钮的使用方法
2017/12/18 Javascript
在Vuex使用dispatch和commit来调用mutations的区别详解
2018/09/18 Javascript
vue3.0 CLI - 3.2 路由的初级使用教程
2018/09/20 Javascript
JS 正则表达式验证密码、邮箱格式的实例代码
2018/10/28 Javascript
[52:39]完美世界DOTA2联赛PWL S3 CPG vs Forest 第一场 12.16
2020/12/17 DOTA
python魔法方法-自定义序列详解
2016/07/21 Python
Python的对象传递与Copy函数使用详解
2019/12/26 Python
Python3 中sorted() 函数的用法
2020/03/24 Python
python中time tzset()函数实例用法
2021/02/18 Python
Move Free官方海外旗舰店:美国骨关节健康专业品牌
2017/12/06 全球购物
Hawes & Curtis澳大利亚官网:英国经典服饰品牌
2018/10/29 全球购物
致800米运动员广播稿
2014/02/16 职场文书
关于保护环境的标语
2014/06/09 职场文书
二年级数学教学反思
2016/02/16 职场文书