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的web.py框架下的application.py模块
May 02 Python
python实现将html表格转换成CSV文件的方法
Jun 28 Python
Python selenium 父子、兄弟、相邻节点定位方式详解
Sep 15 Python
python的多重继承的理解
Aug 06 Python
pandas系列之DataFrame 行列数据筛选实例
Apr 12 Python
基于Python实现扑克牌面试题
Dec 11 Python
Python2和Python3中@abstractmethod使用方法
Feb 04 Python
python实现的分层随机抽样案例
Feb 25 Python
python能自学吗
Jun 18 Python
Python rabbitMQ如何实现生产消费者模式
Aug 24 Python
用python-webdriver实现自动填表的示例代码
Jan 13 Python
python3实现常见的排序算法(示例代码)
Jul 04 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实现蜘蛛访问日志统计
2013/07/05 PHP
php保存任意网络图片到服务器的方法
2015/04/14 PHP
双冒号 ::在PHP中的使用情况
2015/11/05 PHP
php实现文件管理与基础功能操作
2017/03/21 PHP
PHP排序算法之基数排序(Radix Sort)实例详解
2018/04/21 PHP
PHP echo()函数讲解
2019/02/15 PHP
PHP基于phpqrcode类生成二维码的方法示例详解
2020/08/07 PHP
PHP7变量处理机制修改
2021/03/09 PHP
JavaScript 常见对象类创建代码与优缺点分析
2009/12/07 Javascript
关于jQuery中的end()使用方法
2011/07/10 Javascript
jQuery EasyUI Dialog拖不下来如何解决
2015/09/28 Javascript
js全选按钮的实现方法
2015/11/17 Javascript
js实现可以点击收缩或张开的悬浮窗
2017/09/18 Javascript
在vue中添加Echarts图表的基本使用教程
2017/11/22 Javascript
bootstrap里bootstrap动态加载下拉框的实例讲解
2018/08/10 Javascript
JavaScript变量提升和严格模式实例分析
2019/01/27 Javascript
vue+elementUI实现图片上传功能
2019/08/20 Javascript
基于JavaScript 实现拖放功能
2019/09/12 Javascript
vue element-ui读取pdf文件的方法
2019/11/26 Javascript
使用nodejs实现JSON文件自动转Excel的工具(推荐)
2020/06/24 NodeJs
vue组件开发之tab切换组件使用详解
2020/08/21 Javascript
[01:12:08]LGD vs OG 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.24
2019/09/10 DOTA
pandas进行数据的交集与并集方式的数据合并方法
2018/06/27 Python
python中使用print输出中文的方法
2018/07/16 Python
对python mayavi三维绘图的实现详解
2019/01/08 Python
python占位符输入方式实例
2019/05/27 Python
python3 webp转gif格式的实现示例
2019/12/10 Python
Pycharm操作Git及GitHub的步骤详解
2020/10/27 Python
解决pycharm修改代码后第一次运行不生效的问题
2021/02/06 Python
动漫专业高职生职业生涯规划书
2014/02/15 职场文书
乡村卫生服务一体化管理实施方案
2014/03/30 职场文书
青春励志演讲稿
2014/04/29 职场文书
纪检干部对照检查材料
2014/08/22 职场文书
个人自荐书怎么写
2015/03/26 职场文书
升职感谢领导的话语及升职感谢信
2019/06/24 职场文书
win11无法添加打印机怎么办? 提示windows无法打开添加打印机的解决办法
2022/04/05 数码科技