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中使用列表生成式的教程
Apr 27 Python
python thrift搭建服务端和客户端测试程序
Jan 17 Python
python try except 捕获所有异常的实例
Oct 18 Python
通过pykafka接收Kafka消息队列的方法
Dec 27 Python
Python2比较当前图片跟图库哪个图片相似的方法示例
Sep 28 Python
分享PyCharm的几个使用技巧
Nov 10 Python
python实现小世界网络生成
Nov 21 Python
关于numpy中eye和identity的区别详解
Nov 29 Python
python中的线程threading.Thread()使用详解
Dec 17 Python
Python字典生成式、集合生成式、生成器用法实例分析
Jan 07 Python
pandas中的数据去重处理的实现方法
Feb 10 Python
Python加密技术之RSA加密解密的实现
Apr 08 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
业余方法DIY电子管FM收音机
2021/03/02 无线电
php 变量未定义等错误的解决方法
2011/01/12 PHP
PHP 读取Postgresql中的数组
2013/04/14 PHP
基于php无限分类的深入理解
2013/06/02 PHP
ThinkPHP中session函数详解
2016/09/14 PHP
动态改变textbox的宽高的js
2006/10/26 Javascript
很可爱的输入框
2008/08/03 Javascript
jQuery前端分页示例分享
2015/02/10 Javascript
jquery validate和jquery form 插件组合实现验证表单后AJAX提交
2015/08/26 Javascript
Jquery 效果使用详解
2015/11/23 Javascript
旺旺在线客服代码 旺旺客服代码生成器
2018/01/09 Javascript
vue把输入框的内容添加到页面的实例讲解
2019/11/11 Javascript
微信小程序swiper使用网络图片不显示问题解决
2019/12/13 Javascript
2019年度web前端面试题总结(主要为Vue面试题)
2020/01/12 Javascript
浅谈js中的attributes和Attribute的用法与区别
2020/07/16 Javascript
Python实现控制台输入密码的方法
2015/05/29 Python
Ruby使用eventmachine为HTTP服务器添加文件下载功能
2016/04/20 Python
用Python写一个无界面的2048小游戏
2016/05/24 Python
Python实现FTP文件传输的实例
2019/07/07 Python
python2爬取百度贴吧指定关键字和图片代码实例
2019/08/14 Python
python 普通克里金(Kriging)法的实现
2019/12/19 Python
Python使用GitPython操作Git版本库的方法
2020/02/29 Python
win10从零安装配置pytorch全过程图文详解
2020/05/08 Python
Keras中 ImageDataGenerator函数的参数用法
2020/07/03 Python
python遍历路径破解表单的示例
2020/11/21 Python
使用纯HTML5编写一款网页上的时钟的代码分享
2015/11/16 HTML / CSS
AmazeUI图片轮播效果的示例代码
2020/08/20 HTML / CSS
NARS化妆品官方商店:美国彩妆品牌
2017/08/26 全球购物
Mio Skincare法国官网:身体紧致及孕期身体护理
2018/04/04 全球购物
工程造价管理专业大专生求职信
2013/10/06 职场文书
搬家公司的创业计划书
2014/01/01 职场文书
促销活动总结怎么写
2014/06/25 职场文书
父亲节活动策划方案
2014/08/24 职场文书
uwsgi+nginx代理Django无法访问静态资源的解决
2021/05/10 Servers
Netty结合Protobuf进行编解码的方法
2021/06/26 Java/Android
Python 居然可以在 Excel 中画画你知道吗
2022/02/15 Python