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缩进区别分析
Feb 15 Python
python爬虫常用的模块分析
Aug 29 Python
Linux系统上Nginx+Python的web.py与Django框架环境
Dec 25 Python
Python GUI Tkinter简单实现个性签名设计
Jun 19 Python
对python中array.sum(axis=?)的用法介绍
Jun 28 Python
Python中的sys.stdout.write实现打印刷新功能
Feb 21 Python
Python 改变数组类型为uint8的实现
Apr 09 Python
利用python绘制中国地图(含省界、河流等)
Sep 21 Python
Python的collections模块真的很好用
Mar 01 Python
python如何做代码性能分析
Apr 26 Python
Pytorch 如何加速Dataloader提升数据读取速度
May 28 Python
python实现学生信息管理系统(面向对象)
Jun 05 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
国内咖啡文化
2021/03/03 咖啡文化
Ajax+PHP 边学边练 之二 实例
2009/11/24 PHP
PHP utf-8编码问题,utf8编码,数据库乱码,页面显示输出乱码
2013/04/08 PHP
php cli模式学习(PHP命令行模式)
2013/06/03 PHP
简单的php购物车代码
2020/06/05 PHP
javascript下数值型比较难点说明
2010/06/07 Javascript
自用js开发框架小成 学习js的朋友可以看看
2010/11/16 Javascript
javascript操作table(insertRow,deleteRow,insertCell,deleteCell方法详解)
2013/12/16 Javascript
JS字符串拼接在ie中都报错的解决方法
2014/03/27 Javascript
纯js代码实现未知宽高的元素在指定元素中垂直水平居中显示
2015/09/12 Javascript
JavaScript动态设置div的样式的方法
2015/12/26 Javascript
给angular加上动画效遇到的问题总结
2016/02/17 Javascript
JS平滑无缝滚动效果的实现代码
2016/05/06 Javascript
jQuery中使用animate自定义动画的方法
2016/05/29 Javascript
vue.js指令v-model使用方法
2017/03/20 Javascript
基于JavaScript实现焦点图轮播效果
2017/03/27 Javascript
在NPM发布自己造的轮子的方法步骤
2019/03/09 Javascript
JavaScript惰性载入函数实例分析
2019/03/27 Javascript
Vue全局loading及错误提示的思路与实现
2019/08/09 Javascript
JavaScript实现图片上传并预览并提交ajax
2019/09/30 Javascript
JS实现基本的网页计算器功能示例
2020/01/16 Javascript
[12:29]《一刀刀一天》之DOTA全时刻19:蝙蝠骑士田伯光再度不举
2014/06/10 DOTA
python 中的列表解析和生成表达式
2011/03/10 Python
剖析Django中模版标签的解析与参数传递
2015/07/21 Python
Saltstack快速入门简单汇总
2016/03/01 Python
Python实现的自定义多线程多进程类示例
2018/03/23 Python
对python 生成拼接xml报文的示例详解
2018/12/28 Python
在Pycharm terminal中字体大小设置的方法
2019/01/16 Python
pyspark给dataframe增加新的一列的实现示例
2020/04/24 Python
基于plt.title无法显示中文的快速解决
2020/05/16 Python
一款恶搞头像特效的制作过程 利用css3和jquery
2014/11/21 HTML / CSS
使用css3 属性如何丰富图片样式(圆角 阴影 渐变)
2012/11/22 HTML / CSS
聚网科技C++面试笔试题
2015/09/01 面试题
标准导师推荐信(医学类)
2013/10/28 职场文书
python基础之文件处理知识总结
2021/05/23 Python
Python安装及建立虚拟环境的完整步骤
2022/06/25 Servers