觉得这篇文章针对python的默认参数写的不错,翻译的也不错,故转载下。

原文链接: Amir Rachum   翻译: 伯乐在线伯乐在线读者
译文链接: http://blog.jobbole.com/42706/

译文如下:

在之前几个月里,我教一些不了解Python的孩子来慢慢熟悉这门语言。渐渐地,我发现了一些几乎所有Python初学者都会犯的错误,所以我决定跟来跟大家分享我的建议。这个系列的每个部分都会关注不同的常见错误,描述如何产生这种错误的,并且提供解决的方法。

用一个可变的值作为默认值

这是一个绝对值得放在第一个来说的问题。不仅仅是因为产生这种BUG的原因很微妙,而且这种问题也很难检查出来。思考一下下面的代码片段:

1
2
3
def foo(numbers=[]):
    numbers.append(9)
    print numbers

在这里,我们定义了一个 list (默认为空),给它加入9并且打印出来。

1
2
3
4
5
6
>>> foo()
[9]
>>> foo(numbers=[1,2])
[1, 2, 9]
>>> foo(numbers=[1,2,3])
[1, 2, 3, 9]

看起来还行吧?可是当我们不输入number 参数来调用 foo 函数时,神奇的事情发生了:

1
2
3
4
5
6
7
8
>>> 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里面保存的初始值的记录。

那么解决方案如下:

1
2
3
4
5
def foo(numbers=None):
    if numbers is None:
        numbers = []
    numbers.append(9)
    print numbers

通常,当人们听到这里,大家会问另一个关于默认值的问题。思考下面的程序:

1
2
3
def foo(count=0):
    count += 1
    print count

当我们运行它的时候,其结果完全是我们期望的:

1
2
3
4
5
6
7
8
9
10
>>> foo()
1
>>> foo()
1
>>> foo(2)
3
>>> foo(3)
4
>>> foo()
1

这又是为啥呢?其秘密不在与默认值被赋值的时候,而是这个默认值本身。整型是一种不可变的变量。跟 list 类型不同,在函数执行的过程中,整型变量是不能被改变的。当我们执行 count+=1 这句话时,我们并没有改变 count 这个变量原有的值。而是让 count 指向了不同的值。可是,当我们执行 numbers.append(9) 的时候,我们改变了原有的 list 。因而导致了这种结果。

下面是在函数里使用默认值时会碰到的另一种相同问题:

1
2
def print_now(now=time.time()):
    print now

跟前面一样,time.time() 的值是可变的,那么它只会在函数定义的时候计算,所以无论调用多少次,都会返回相同的事件 — 这里输出的事件是程序被Python解释运行的时间。

1
2
3
4
5
6
>>> print_now()
1373121487.91
>>> print_now()
1373121487.91
>>> print_now()
1373121487.91

* 这个问题和它的解决方案在 Python 2.x 和 3.x 里都是类似的,在Python 3.x 里面唯一的不同,是里面的print 表达式应该是函数调用的方式(print(numbers))。

* 大家应该注意到我在解决方案里用了 if  numbers is None 而不是 if not numbers 。这是另一种常见的错误,我准备在接下来的文章里面介绍。

Python 新手常犯错误(第一部分)转载的更多相关文章

  1. Python 新手常犯错误

    Python 新手常犯错误(第二部分) 转发自:http://blog.jobbole.com/43826/ 作用域 在这篇文章里,我们来关注作用域在Python被误用的地方.通常,当我们定义了一个全 ...

  2. Python 新手常犯错误(第一部分)

    转载自:http://blog.jobbole.com/42706/ 在之前几个月里,我教一些不了解Python的孩子来慢慢熟悉这门语言.渐渐地,我发现了一些几乎所有Python初学者都会犯的错误,所 ...

  3. Python 新手常犯错误(第二部分)

    转发自:http://blog.jobbole.com/43826/ 在之前几个月里,我教一些不了解Python的孩子来慢慢熟悉这门语言.渐渐地,我发现了一些几乎所有Python初学者都会犯的错误,所 ...

  4. 使用DX绘制3D物体时新手常犯错误,看不见物体时可以一一排查

    1.镜头不对: 物体不在镜头范围内,检查视图矩阵,世界矩阵,投影矩阵. 2.颜色全黑: 打开光照情况下,MATERIAL全为0, 或,在没有打开光照情况下,颜色值为0,造成全黑.检查当前Materia ...

  5. python中常犯错误之字符串列表下标问题

    下标用得是中括号[] 不是小括号() 1,python中的小括号( ):代表tuple元组数据类型,元组是一种不可变序列.创建方法很简单,大多时候都是用小括号括起来的. 2.python中的中括号[ ...

  6. 零基础怎么学Python编程,新手常犯哪些错误?

    Python是人工智能时代最佳的编程语言,入门简单.功能强大,深获初学者的喜爱. 很多零基础学习Python开发的人都会忽视一些小细节,进而导致整个程序出现错误.下面就给大家介绍一下Python开发者 ...

  7. C#新手常犯的错误

    虽然这篇post的标题是新手常犯的错误,实际上很多有经验的程序员也经常犯这些错误,我整理了一下,就当是笔记.1.遍历List的错误,比如如下代码: List<String> strList ...

  8. scanf()常犯错误

    ------------------------------------------------------------------------ <> 本意:接收字符串. 写成代码:voi ...

  9. Python开发最常犯错误总结10种

    不管是在学习还是工作过程中,人都会犯错.虽然Python的语法简单.灵活,但也一样存在一些不小的坑,一不小心,初学者和资深Python程序员都有可能会栽跟头.本文是Toptal网站的程序员梳理的10大 ...

随机推荐

  1. python爬虫<urlopen error [Errno 10061] >

    在网上看了十几篇文章,都是说的是IE的代理设置,具体是: Tools->Internet Options->Connections->Lan Settings 将代理服务器的小勾勾去 ...

  2. ASP.NET MVC - The view must derive from WebViewPage, or WebViewPage<TModel>

    当通过一个空的站点构建ASP.NET MVC时经常会出现各种配置缺少的问题,最简单但的办法是吧VS自动生成的web.config文件拷贝到对应的目录下面 The view must derive fr ...

  3. Docker 使用及常用命令

    https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile/expose.html docker practice 资料 -- ...

  4. log4net写txt日志

    1.配置: <configSections>节点下添加: <section name="log4net" type="log4net.Config.Lo ...

  5. java动态代理中的invoke方法是如何被自动调用的(转)

    一.动态代理与静态代理的区别. (1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大: (2)可以实现AOP编程,这是静态代理无法实现的: (3)解耦,如果用在web业务下,可以实现数据层 ...

  6. 部分常用dos命令

    Microsoft Windows XP [版本 ] (C) 版权所有 - Microsoft Corp. C:\Documents and Settings\Administrator>d: ...

  7. 【BZOJ4231】回忆树 离线+fail树+KMP

    [BZOJ4231]回忆树 Description 回忆树是树. 具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母). 对一棵回忆树来说 ...

  8. [Gradle] 给已存在的 task 添加依赖

    需求:在编译宿主 APP 之前先编译两个插件 SamplePlugin1 和 SamplePlugin2 tasks.whenTaskAdded { task -> if (task.name ...

  9. SaltStack自动化安装zabbix-server

    使用SaltStack自动化安装zabbix-server 1,设置ntp时间同步 2,安装zabbix-agent 3,安装zabbix-server 4,安装及配置mariadb(mariadb与 ...

  10. CLR via 笔记4.2 类型转换 is 与 as 区别

    is 和 as 操作符是用来进行强制类型转换的 is : 检查一个对象是否兼容于其他指定的类型,并返回一个Bool值,永远不会抛出异常 object o = new object(); if (o i ...