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 相关文章推荐
Django的数据模型访问多对多键值的方法
Jul 21 Python
python 多线程实现检测服务器在线情况
Nov 25 Python
关于Python数据结构中字典的心得
Dec 04 Python
Python数据分析库pandas基本操作方法
Apr 08 Python
Django Admin实现三级联动的示例代码(省市区)
Jun 22 Python
对pycharm代码整体左移和右移缩进快捷键的介绍
Jul 16 Python
python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例
Jun 17 Python
python实现批量nii文件转换为png图像
Jul 18 Python
python实现遍历文件夹图片并重命名
Mar 23 Python
jupyter notebook 恢复误删单元格或者历史代码的实现
Apr 17 Python
python mysql自增字段AUTO_INCREMENT值的修改方式
May 18 Python
python 字典和列表嵌套用法详解
Jun 29 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 中文和编码判断代码
2010/05/16 PHP
PHP实现今天是星期几的几种写法
2013/09/26 PHP
使用配置类定义Codeigniter全局变量
2014/06/12 PHP
php自动获取关键字的方法
2015/01/06 PHP
Laravel中使用自己编写类库的3种方法
2015/02/10 PHP
thinkphp中的多表关联查询的实例详解
2017/10/12 PHP
PHP7 标准库修改
2021/03/09 PHP
JavaScript 验证浏览器是否支持javascript的方法小结
2009/05/17 Javascript
js取消单选按钮选中并判断对象是否为空
2013/11/14 Javascript
js数值和和字符串进行转换时可以对不同进制进行操作
2014/03/05 Javascript
JavaScript转换二进制编码为ASCII码的方法
2015/04/16 Javascript
jQuery实现图片渐入渐出切换展示效果
2015/08/15 Javascript
jQuery插件实现无缝滚动特效
2015/11/24 Javascript
延时加载JavaScript代码提高速度
2015/12/27 Javascript
Highcharts 多个Y轴动态刷新数据的实现代码
2016/05/28 Javascript
[53:15]Mineski vs iG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python抓取豆瓣图片并自动保存示例学习
2014/01/10 Python
python操作数据库之sqlite3打开数据库、删除、修改示例
2014/03/13 Python
使用Python内置的模块与函数进行不同进制的数的转换
2016/03/12 Python
python中字典按键或键值排序的实现代码
2019/08/27 Python
Python模块future用法原理详解
2020/01/20 Python
将pytorch转成longtensor的简单方法
2020/02/18 Python
Python datetime模块的使用示例
2021/02/02 Python
利用Python实现最小二乘法与梯度下降算法
2021/02/21 Python
html5的websockets全双工通信详解学习示例
2014/02/26 HTML / CSS
浅谈Html5多线程开发之WebWorkers
2018/05/02 HTML / CSS
NARS化妆品官方商店:美国彩妆品牌
2017/08/26 全球购物
应用数学专业求职信
2014/03/14 职场文书
党员志愿者活动方案
2014/08/28 职场文书
报到证办理个人委托书
2014/10/06 职场文书
2015大学迎新标语
2015/07/16 职场文书
小学总务工作总结
2015/08/13 职场文书
给校长的建议书作文300字
2015/09/14 职场文书
2019年暑期安全广播稿!
2019/07/03 职场文书
搭建Yolov5服务器
2022/04/30 Servers
Android RecyclerView实现九宫格效果
2022/06/28 Java/Android