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多线程和队列操作实例
Jun 21 Python
python 根据正则表达式提取指定的内容实例详解
Dec 04 Python
Python scikit-learn 做线性回归的示例代码
Nov 01 Python
在Python中预先初始化列表内容和长度的实现
Nov 28 Python
浅谈tensorflow 中tf.concat()的使用
Feb 07 Python
python 实现读取csv数据,分类求和 再写进 csv
May 18 Python
Python3 Tensorlfow:增加或者减小矩阵维度的实现
May 22 Python
用Python开发app后端有优势吗
Jun 29 Python
记一次django内存异常排查及解决方法
Aug 07 Python
pytorch 中forward 的用法与解释说明
Feb 26 Python
手把手教你实现PyTorch的MNIST数据集
Jun 28 Python
Python列表的索引与切片
Apr 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
受疫情影响 动画《Re从零开始的异世界生活》第二季延期至7月
2020/03/10 日漫
JAVA/JSP学习系列之四
2006/10/09 PHP
PHP 5.0对象模型深度探索之对象复制
2008/03/27 PHP
PHP 导出Excel示例分享
2014/08/18 PHP
两种php给图片加水印的实现代码
2020/04/18 PHP
php基于闭包实现函数的自调用(递归)实例分析
2016/11/11 PHP
php实现的顺序线性表示例
2019/05/04 PHP
js 距离某一时间点时间是多少实现代码
2013/10/14 Javascript
javascript中typeof的使用示例
2013/12/19 Javascript
js 数值转换为3位逗号分隔的示例代码
2014/02/19 Javascript
JS动画效果打开、关闭层的实现方法
2015/05/09 Javascript
JavaScript与HTML的结合方法详解
2015/11/23 Javascript
使用JS实现图片展示瀑布流效果(简单实例)
2016/09/06 Javascript
微信小程序  wx.request合法域名配置详解
2016/11/23 Javascript
文件上传插件SWFUpload的使用指南
2016/11/29 Javascript
jQuery插件HighCharts绘制2D柱状图、折线图和饼图的组合图效果示例【附demo源码下载】
2017/03/09 Javascript
vue-cli3全面配置详解
2018/11/14 Javascript
JS函数本身的作用域实例分析
2020/03/16 Javascript
vue2.x数组劫持原理的实现
2020/04/19 Javascript
详解vue 组件注册
2020/11/20 Vue.js
pygame学习笔记(1):矩形、圆型画图实例
2015/04/15 Python
Python中的字典与成员运算符初步探究
2015/10/13 Python
详解Python中heapq模块的用法
2016/06/28 Python
python 对字典按照value进行排序的方法
2019/05/09 Python
Python将主机名转换为IP地址的方法
2019/08/14 Python
Python3 使用pillow库生成随机验证码
2019/08/26 Python
python求绝对值的三种方法小结
2019/12/04 Python
俄罗斯电动工具和设备购物网站:Vseinstrumenti.ru
2020/11/12 全球购物
设计模式的基本要素是什么
2014/04/21 面试题
班主任寄语大全
2014/04/04 职场文书
环卫工人先进事迹材料
2014/06/02 职场文书
团党委领导干部党的群众路线教育实践活动个人对照检查材料思想汇
2014/10/05 职场文书
实习生矿工检讨书
2014/10/13 职场文书
党的群众路线教育实践活动批评与自我批评范文
2014/10/16 职场文书
信访维稳工作汇报
2014/10/27 职场文书
5道关于python基础 while循环练习题
2021/11/27 Python