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 相关文章推荐
使用go和python递归删除.ds store文件的方法
Jan 22 Python
Python编程修改MP3文件名称的方法
Apr 19 Python
pycharm中连接mysql数据库的步骤详解
May 02 Python
Python实现文件信息进行合并实例代码
Jan 17 Python
python使用tkinter实现简单计算器
Jan 30 Python
python 求某条线上特定x值或y值的点坐标方法
Jul 09 Python
使用Python实现图像标记点的坐标输出功能
Aug 14 Python
Python imread、newaxis用法详解
Nov 04 Python
对python中 math模块下 atan 和 atan2的区别详解
Jan 17 Python
基于python实现删除指定文件类型
Jul 21 Python
基于python实现操作git过程代码解析
Jul 27 Python
Matlab使用Plot函数实现数据动态显示方法总结
Feb 25 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
怎样在UNIX系统下安装php3
2006/10/09 PHP
PHP Socket 编程
2010/04/09 PHP
php批量缩放图片的代码[ini参数控制]
2011/02/11 PHP
php curl常见错误:SSL错误、bool(false)
2011/12/28 PHP
PHP 第二节 数据类型之数组
2012/04/28 PHP
php基于双向循环队列实现历史记录的前进后退等功能
2015/08/08 PHP
PHP简单创建压缩图的方法
2016/08/24 PHP
php设计模式之职责链模式定义与用法经典示例
2019/09/19 PHP
浅谈Laravel中的三种中间件的作用
2019/10/13 PHP
jquery获取当前点击对象的value方法
2014/02/28 Javascript
JavaScript清空数组元素的两种方法简单比较
2015/07/10 Javascript
js和C# 时间日期格式转换的简单实例
2016/05/28 Javascript
详解原生js实现offset方法
2017/06/15 Javascript
JS判断微信扫码的方法
2017/08/07 Javascript
微信小程序倒计时功能实现代码
2017/11/09 Javascript
详解angularjs 学习之 scope作用域
2018/01/15 Javascript
Vue实现底部侧边工具栏的实例代码
2018/09/03 Javascript
浅谈react性能优化的方法
2018/09/05 Javascript
深入理解Node内建模块和对象
2019/03/12 Javascript
mapboxgl区划标签避让不遮盖实现的代码详解
2020/07/01 Javascript
Vue中避免滥用this去读取data中数据
2021/03/02 Vue.js
[04:16]DOTA2全国高校联赛16强抽签
2018/05/02 DOTA
Linux下使用python自动修改本机网关代码分享
2015/05/21 Python
Python中pip安装非PyPI官网第三方库的方法
2015/06/02 Python
Python 中 list 的各项操作技巧
2017/04/13 Python
Python运维自动化之nginx配置文件对比操作示例
2018/08/29 Python
opencv3/python 鼠标响应操作详解
2019/12/11 Python
翻转数列python实现,求前n项和,并能输出整个数列的案例
2020/05/03 Python
python中pandas库中DataFrame对行和列的操作使用方法示例
2020/06/14 Python
html5桌面通知(Web Notifications)实例解析
2014/07/07 HTML / CSS
国际政治个人自荐信范文
2013/11/26 职场文书
物业客服专员岗位职责
2013/11/30 职场文书
加拿大留学自荐信
2014/01/28 职场文书
网吧管理制度范本
2015/08/05 职场文书
大学生自我鉴定怎么写
2019/05/07 职场文书
PyTorch 如何自动计算梯度
2021/05/23 Python