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的web框架编写MVC配置来使其运行的教程
Apr 30 Python
python实现百万答题自动百度搜索答案
Jan 16 Python
一行代码让 Python 的运行速度提高100倍
Oct 08 Python
python中yield的用法详解——最简单,最清晰的解释
Apr 04 Python
解决安装python3.7.4报错Can''t connect to HTTPS URL because the SSL module is not available
Jul 31 Python
Python中顺序表原理与实现方法详解
Dec 03 Python
tensorflow将图片保存为tfrecord和tfrecord的读取方式
Feb 17 Python
Python异常原理及异常捕捉实现过程解析
Mar 25 Python
通俗易懂了解Python装饰器原理
Sep 17 Python
python实现无边框进度条的实例代码
Dec 30 Python
python SOCKET编程基础入门
Feb 27 Python
python 开心网和豆瓣日记爬取的小爬虫
May 29 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 empty函数 使用说明
2009/08/10 PHP
测试php函数的方法
2013/11/13 PHP
Laravel接收前端ajax传来的数据的实例代码
2017/07/20 PHP
JQuery困惑—包装集 DOM节点
2009/10/16 Javascript
面向对象的编程思想在javascript中的运用上部
2009/11/20 Javascript
javascript温习的一些笔记 基础常用知识小结
2011/06/22 Javascript
jQuery Mobile的loading对话框显示/隐藏方法分享
2013/11/26 Javascript
jquery append()方法与html()方法的区别及使用介绍
2014/08/01 Javascript
javascript中setInterval的用法
2015/07/19 Javascript
JavaScript获取function所有参数名的方法
2015/10/30 Javascript
js使用cookie记录用户名的方法
2015/11/26 Javascript
JQuery+EasyUI轻松实现步骤条效果
2016/02/22 Javascript
jQuery ajax调用后台aspx后台文件的两种常见方法(不是ashx)
2016/06/28 Javascript
利用jQuery的动画函数animate实现豌豆发射效果
2016/08/28 Javascript
微信小程序页面间通信的5种方式
2017/03/31 Javascript
vue实现表格数据的增删改查
2017/07/10 Javascript
vue+vuex+axios+echarts画一个动态更新的中国地图的方法
2017/12/19 Javascript
Vue中保存数据到磁盘文件的方法
2018/09/06 Javascript
JavaScript实现shuffle数组洗牌操作示例
2019/01/03 Javascript
Node.js console控制台简单用法分析
2019/01/04 Javascript
浅谈Vue3.0之前你必须知道的TypeScript实战技巧
2019/09/11 Javascript
[43:18]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.22
2019/09/05 DOTA
python自带的http模块详解
2016/11/06 Python
python 实现敏感词过滤的方法
2019/01/21 Python
python 正则表达式贪婪模式与非贪婪模式原理、用法实例分析
2019/10/14 Python
python3 动态模块导入与全局变量使用实例
2019/12/22 Python
基于python实现文件加密功能
2020/01/06 Python
Pycharm debug调试时带参数过程解析
2020/02/03 Python
python shell命令行中import多层目录下的模块操作
2020/03/09 Python
浅谈python3打包与拆包在函数的应用详解
2020/05/02 Python
Html5适配iphoneX刘海屏的简单实现
2019/04/09 HTML / CSS
《燕子专列》教学反思
2014/02/21 职场文书
企业新年寄语
2014/04/04 职场文书
逃课检讨书怎么写
2015/01/01 职场文书
2019最新公司租房合同(例文)
2019/07/18 职场文书
爱心捐款倡议书:点燃希望,传递温暖
2019/11/04 职场文书