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 time模块用法实例详解
Sep 11 Python
python中实现延时回调普通函数示例代码
Sep 08 Python
python flask中静态文件的管理方法
Mar 20 Python
Python for循环中的陷阱详解
Jul 13 Python
Python3中关于cookie的创建与保存
Oct 21 Python
Python面向对象基础入门之设置对象属性
Dec 11 Python
django的聚合函数和aggregate、annotate方法使用详解
Jul 23 Python
用Python解数独的方法示例
Oct 24 Python
Pytorch 实现冻结指定卷积层的参数
Jan 06 Python
python实现将两个文件夹合并至另一个文件夹(制作数据集)
Apr 03 Python
python2.7使用scapy发送syn实例
May 05 Python
jupyter notebook更换皮肤主题的实现
Jan 07 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 之入门篇
2006/12/04 PHP
落伍首发 php+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
2006/12/16 PHP
非常不错的MySQL优化的8条经验
2008/03/24 PHP
php session劫持和防范的方法
2013/11/12 PHP
ThinkPHP的cookie和session冲突造成Cookie不能使用的解决方法
2014/07/01 PHP
PHP+FFMPEG实现将视频自动转码成H264标准Mp4文件
2014/09/24 PHP
Add Formatted Text to a Word Document
2007/06/15 Javascript
script标签的 charset 属性使用说明
2010/12/04 Javascript
js同比例缩放图片的小例子
2013/10/30 Javascript
JS文本获得焦点清除文本文字的示例代码
2014/01/13 Javascript
Bootstrap每天必学之导航组件
2016/04/25 Javascript
JavaScript 闭包机制详解及实例代码
2016/10/10 Javascript
巧用Javascript的逻辑运算符
2016/12/02 Javascript
分分钟学会vue中vuex的应用(入门教程)
2017/09/14 Javascript
AjaxUpLoad.js实现文件上传功能
2018/03/02 Javascript
Jquery如何使用animation动画效果改变背景色的代码
2020/07/20 jQuery
[01:16:37]【全国守擂赛】第三周决赛 Dark Knight vs. 一个弱队
2020/05/04 DOTA
用Python的SimPy库简化复杂的编程模型的介绍
2015/04/13 Python
numpy自动生成数组详解
2017/12/15 Python
python实现守护进程、守护线程、守护非守护并行
2018/05/05 Python
python使用代理ip访问网站的实例
2018/05/07 Python
python使用rpc框架gRPC的方法
2018/08/24 Python
python爬虫爬取微博评论案例详解
2019/03/27 Python
python实现程序重启和系统重启方式
2020/04/16 Python
Python爬虫分析微博热搜关键词的实现代码
2021/02/22 Python
新加坡第一大健康与美容零售商:屈臣氏新加坡(Watsons Singapore)
2020/12/11 全球购物
一份报关员的职业规划范文
2014/01/08 职场文书
函授本科自我鉴定
2014/02/04 职场文书
GMP办公室主任岗位职责
2014/03/14 职场文书
党员一句话承诺大全
2014/03/28 职场文书
小学教师师德整改措施
2014/09/29 职场文书
避暑山庄导游词
2015/02/04 职场文书
横空出世观后感
2015/06/09 职场文书
2016年学校“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书
简述python四种分词工具,盘点哪个更好用?
2021/04/13 Python
详解Python常用的魔法方法
2021/06/03 Python