Python中设置变量作为默认值时容易遇到的错误


Posted in Python onApril 03, 2015

思考一下下面的代码片段:
 

def foo(numbers=[]):
  numbers.append(9)
  print numbers

在这里,我们定义了一个 list (默认为空),给它加入9并且打印出来。
 

>>> foo()
[9]
>>> foo(numbers=[1,2])
[1, 2, 9]
>>> foo(numbers=[1,2,3])
[1, 2, 3, 9]

看起来还行吧?可是当我们不输入number 参数来调用 foo 函数时,神奇的事情发生了:
 

>>> foo() # first time, like before
[9]
>>> foo() # second time
[9, 9]
>>> foo() # third time...
[9, 9, 9]
>>> foo() # WHAT IS THIS BLACK MAGIC?!
[9, 9, 9, 9]

那么,这是神马情况?直觉告诉我们无论我们不输入 number 参数调用 foo 函数多少次,这里的9应该被分配进了一个空的 list。这是错的!在Python里,函数的默认值实在函数定义的时候实例化的,而不是在调用的时候。

那么我们仍然会问,为什么在调用函数的时候这个默认值却被赋予了不同的值?因为在你每次给函数指定一个默认值的时候,Python都会存储这个值。如果在调用函数的时候重写了默认值,那么这个存储的值就不会被使用。当你不重写默认值的时候,那么Python就会让默认值引用存储的值(这个例子里的numbers)。它并不是将存储的值拷贝来为这个变量赋值。这个概念可能对初学者来说,理解起来会比较吃力,所以可以这样来理解:有两个变量,一个是内部的,一个是当前运行时的变量。现实就是我们有两个变量来用相同的值进行交互,所以一旦 numbers 的值发生变化,也会改变Python里面保存的初始值的记录。

那么解决方案如下:
 

def foo(numbers=None):
  if numbers is None:
    numbers = []
  numbers.append(9)
  print numbers

通常,当人们听到这里,大家会问另一个关于默认值的问题。思考下面的程序:
 

def foo(count=0):
  count += 1
  print count

当我们运行它的时候,其结果完全是我们期望的:
 

>>> foo()
1
>>> foo()
1
>>> foo(2)
3
>>> foo(3)
4
>>> foo()
1

这又是为啥呢?其秘密不在与默认值被赋值的时候,而是这个默认值本身。整型是一种不可变的变量。跟 list 类型不同,在函数执行的过程中,整型变量是不能被改变的。当我们执行 count+=1 这句话时,我们并没有改变 count 这个变量原有的值。而是让 count 指向了不同的值。可是,当我们执行 numbers.append(9) 的时候,我们改变了原有的 list 。因而导致了这种结果。

下面是在函数里使用默认值时会碰到的另一种相同问题:
 

def print_now(now=time.time()):
  print now

跟前面一样,time.time() 的值是可变的,那么它只会在函数定义的时候计算,所以无论调用多少次,都会返回相同的时间 — 这里输出的时间是程序被Python解释运行的时间。

>>> print_now()
1373121487.91
>>> print_now()
1373121487.91
>>> print_now()
1373121487.91

* 这个问题和它的解决方案在 Python 2.x 和 3.x 里都是类似的,在Python 3.x 里面唯一的不同,是里面的print 表达式应该是函数调用的方式(print(numbers))。

Python 相关文章推荐
Python 的 with 语句详解
Jun 13 Python
在Python中利用Pandas库处理大数据的简单介绍
Apr 07 Python
Python实现随机生成手机号及正则验证手机号的方法
Apr 25 Python
对python的bytes类型数据split分割切片方法
Dec 04 Python
Python实现计算对象的内存大小示例
Jul 10 Python
关于Tensorflow 模型持久化详解
Feb 12 Python
从0到1使用python开发一个半自动答题小程序的实现
May 12 Python
python如何安装下载后的模块
Jul 03 Python
python批量修改文件名的示例
Sep 27 Python
python如何调用php文件中的函数详解
Dec 29 Python
python用tkinter开发的扫雷游戏
Jun 01 Python
详解Python中的进程和线程
Jun 23 Python
用Python编写一个简单的Lisp解释器的教程
Apr 03 #Python
举例讲解Python中is和id的用法
Apr 03 #Python
详解Python2.x中对Unicode编码的使用
Apr 03 #Python
对于Python中线程问题的简单讲解
Apr 03 #Python
python BeautifulSoup设置页面编码的方法
Apr 03 #Python
用Python编写一个简单的FUSE文件系统的教程
Apr 02 #Python
用Python中的__slots__缓存资源以节省内存开销的方法
Apr 02 #Python
You might like
php中3des加密代码(完全与.net中的兼容)
2012/08/02 PHP
php写入数据到CSV文件的方法
2015/03/14 PHP
PHP使用curl函数发送Post请求的注意事项
2016/11/26 PHP
PHP两种实现无级递归分类的方法
2017/03/02 PHP
代码精简的可以实现元素圆角的js函数
2007/07/21 Javascript
jquery 页面全选框实践代码
2010/04/02 Javascript
jQuery javaScript捕获回车事件(示例代码)
2013/11/07 Javascript
js加入收藏夹代码(兼容ie/ff/op)
2014/05/16 Javascript
告诉你什么是javascript的回调函数
2014/09/04 Javascript
js读取csv文件并使用json显示出来
2015/01/09 Javascript
jQuery层级选择器用法分析
2015/02/10 Javascript
JavaScript基于setTimeout实现计数的方法
2015/05/08 Javascript
JS实现网页游戏中滑块响应鼠标点击移动效果
2015/10/19 Javascript
Jquery对新插入的节点 绑定Click事件失效的解决方法
2016/06/02 Javascript
BootStrap的两种模态框方式
2017/05/10 Javascript
微信小程序 侧滑删除(左滑删除)
2017/05/23 Javascript
详解JavaScript中的六种错误类型
2017/09/21 Javascript
layer.confirm()右边按钮实现href的例子
2019/09/27 Javascript
微信jssdk踩坑之签名错误invalid signature
2020/05/19 Javascript
[00:39]DOTA2上海特级锦标赛 Liquid战队宣传片
2016/03/04 DOTA
深入浅析ImageMagick命令执行漏洞
2016/10/11 Python
如何在sae中设置django,让sae的工作环境跟本地python环境一致
2017/11/21 Python
python实现自动登录后台管理系统
2018/10/18 Python
serialVersionUID具有什么样的特征
2014/02/20 面试题
药物学专业学生的自我评价
2013/10/27 职场文书
半年思想汇报
2013/12/30 职场文书
项目计划书范文
2014/01/09 职场文书
银行领导证婚词
2014/01/11 职场文书
献爱心活动总结
2014/05/07 职场文书
大学生简历求职信
2014/06/24 职场文书
委托书英文
2015/01/28 职场文书
幼儿园园长个人总结
2015/03/02 职场文书
教学质量月活动总结
2015/05/11 职场文书
无罪辩护词范文
2015/05/21 职场文书
python使用opencv对图像添加噪声(高斯/椒盐/泊松/斑点)
2022/04/06 Python
nginx代理实现静态资源访问的示例代码
2022/07/07 Servers