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赋值操作方法分享
Mar 23 Python
Python Socket编程入门教程
Jul 11 Python
Python中List.count()方法的使用教程
May 20 Python
基python实现多线程网页爬虫
Sep 06 Python
Python 记录日志的灵活性和可配置性介绍
Feb 27 Python
python中csv文件的若干读写方法小结
Jul 04 Python
python 解决动态的定义变量名,并给其赋值的方法(大数据处理)
Nov 10 Python
用Python实现最速下降法求极值的方法
Jul 10 Python
PyQT5 emit 和 connect的用法详解
Dec 13 Python
通过实例了解python__slots__使用方法
Sep 14 Python
五种Python转义表示法
Nov 27 Python
Python 使用SFTP和FTP实现对服务器的文件下载功能
Dec 17 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
模拟xcopy的函数
2006/10/09 PHP
php检测图片木马多进制编程实践
2013/04/11 PHP
yii框架无限极分类的实现方法
2017/04/08 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
Yii框架实现对数据库的CURD操作示例
2019/09/03 PHP
PHP保存Base64图片base64_decode的问题整理
2019/11/04 PHP
Mootools 1.2教程 Tooltips
2009/09/15 Javascript
jQuery中fadeIn、fadeOut、fadeTo的使用方法(图片显示与隐藏)
2013/05/08 Javascript
JS动态创建Table,Tr,Td并赋值的具体实现
2013/07/05 Javascript
轻量级javascript 框架Backbone使用指南
2015/07/24 Javascript
JS实现消息来时让网页标题闪动效果的方法
2016/04/20 Javascript
jQuery插件EasyUI获取当前Tab中iframe窗体对象的方法
2016/08/05 Javascript
Vuejs第六篇之Vuejs与form元素实例解析
2016/09/05 Javascript
Nodejs进阶:express+session实现简易登录身份认证
2017/04/24 NodeJs
Ajax高级笔记 JavaScript高级程序设计笔记
2017/06/22 Javascript
原生js实现密码输入框值的显示隐藏
2017/07/17 Javascript
js最简单的双向绑定实例讲解
2018/01/02 Javascript
vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能
2018/03/01 Javascript
详解在vue-cli项目下简单使用mockjs模拟数据
2018/10/19 Javascript
分享5个小技巧让你写出更好的 JavaScript 条件语句
2018/10/20 Javascript
Vue.js组件间通信方式总结【推荐】
2018/11/23 Javascript
JavaScript设计模式之享元模式实例详解
2019/01/17 Javascript
vue-router结合vuex实现用户权限控制功能
2019/11/14 Javascript
手把手教您实现react异步加载高阶组件
2020/04/07 Javascript
使用JavaScript实现网页秒表功能(含开始、暂停、继续、重置功能)
2020/06/05 Javascript
[01:08:32]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第二场 1月18日
2021/03/11 DOTA
Python基于tkinter模块实现的改名小工具示例
2017/07/27 Python
Python实现利用163邮箱远程关电脑脚本
2018/02/22 Python
Python实现简单的列表冒泡排序和反转列表操作示例
2019/07/10 Python
WWE美国职业摔角官方商店:WWE Shop
2018/11/15 全球购物
机电一体化专业求职信
2014/07/22 职场文书
民族团结好少年事迹材料
2014/08/19 职场文书
乡镇四风对照检查材料
2014/08/31 职场文书
党的群众路线批评与自我批评范文
2014/10/16 职场文书
Go语言中break label与goto label的区别
2021/04/28 Golang
python中subplot大小的设置步骤
2021/06/28 Python