写python的都知道,python函数或者方法可以使用默认参数,比如

 def foo(arg=None):
print(arg) foo() foo("hello world")

一个很简单的函数,参数arg默认使用None,当调用foo函数时,可以传入一个参数,也可以不传入参数,运行结果如下

None
hello world

这很好理解。默认参数是python一个很好的特性。

但是如果使用可变对象作为默认参数,就会引发问题。之前写过一个脚本,bug不断,后来终于找到了原因。下面引用Fluent python中的一个例子加以说明。

废话不多说,直接上代码

 """可变对象作为默认参数传入到函数引发的问题"""

 class Bus:
def __init__(self, passengers=[]):
self.passengers = passengers def pick(self, a_passenger):
self.passengers.append(a_passenger) def drop(self, a_passenger):
self.passengers.remove(a_passenger) if __name__ == '__main__':
bus1 = Bus(['Alice', 'Jeff', 'ethan'])
print(bus1.passengers)
bus1.pick("Simon")
print(bus1.passengers) # 到此为止并没什么问题 # 接下来问题来了, 创建了两个Bus对象,并进行操作。
bus2 = Bus()
print(bus2.passengers)
bus2.pick("Alice") # "Alice"上了bus2
print(bus2.passengers) # 打印bus2的乘客列表
bus3 = Bus() # 创建另一个Bus实例,同样不传入参数(使用默认[]参数)
print(bus3.passengers) # 打印bus3的乘客列表 bus3.pick("alex")
print(bus3.passengers)
print(bus2.passengers)
print(bus3.passengers is bus2.passengers) print(bus3.passengers is bus1.passengers)

下面来看运行结果

['Alice', 'Jeff', 'ethan']
['Alice', 'Jeff', 'ethan', 'Simon']
[]
['Alice']
['Alice']
['Alice', 'alex']
['Alice', 'alex']
True
False

从结果可以看出,bus1这个对象似乎没什么问题,而bus2和bus3对象的passengers属性似乎是同一个。为什么会这样呢?

我们先来分析下Bus这个类

定义了一个Bus类,有一个__init__方法,pick方法和drop方法。__init__方法有一个默认参数,即passengers=[]。

大家应该都知道在python中列表对象([])是可变对象。

当创建bus1对象的时候,传入了实实在在的passengers参数(一个非空列表)

当创建bus2和bus3对象的时候,没有传入参数,所以会使用默认参数([])

然后我对这几个Bus实例进行操作(调用pick或drop方法,即插入或者删除passengers列表中的乘客)

从结果可以很直观的看出来,bus1.passengers 并没什么问题,一切按照我们的预期进行。但是bus2的乘客"Alice"为什么会出现在bus3上? bus3的乘客“alex”为什么又会出现在bus2上???

其实这就是用可变对象作为默认参数引发的后果。因为当使用默认参数定义了一个函数/方法之后,加载模块的时候已经初始化了这个可变对象,所以当你调用这个函数/方法的时候,如果你未传入参数(即使用了默认参数), 则对函数/方法的不同调用,其实传入的是同一个可变对象(上面例子中是一个空列表)。

下面请看

 class Bus:
def __init__(self, passengers=[]):
self.passengers = passengers def pick(self, a_passenger):
self.passengers.append(a_passenger) def drop(self, a_passenger):
self.passengers.remove(a_passenger) if __name__ == '__main__':
print(Bus.__init__.__defaults__)

运行结果

([],)
在这个例子中,并没有初始化Bus类, 而__init__方法的__defaults__属性,是一个空列表, 也就是说,在完成__init__方法定义的时候,就已经初始化了默认参数(一个空列表), 因为列表是可变对象,所以后续对Bus类的引用就会引发问题。

至此,问题原因很明确了,就是可变对象导致了这个问题,因此千万不要使用可变对象作为python函数/方法的默认参数, 一般建议使用None作为默认参数,即
 def foo(arg=None):
pass
												

使用可变对象作为python函数默认参数引发的问题的更多相关文章

  1. 【转】Python函数默认参数陷阱

    [转]Python函数默认参数陷阱 阅读目录 可变对象与不可变对象 函数默认参数陷阱 默认参数原理 避免 修饰器方法 扩展 参考 请看如下一段程序: def extend_list(v, li=[]) ...

  2. Python面试题目之Python函数默认参数陷阱

    请看如下一段程序: def extend_list(v, li=[]): li.append(v) return li list1 = extend_list(10) list2 = extend_l ...

  3. python函数默认参数为可变对象的理解

    1.代码在执行的过程中,遇到函数定义,初始化函数生成存储函数名,默认参数初识值,函数地址的函数对象. 2.代码执行不在初始化函数,而是直接执行函数体. 代码实例 这要从函数的特性说起,在 Python ...

  4. python函数默认参数陷阱

    对于学习python的人都有这样的困惑 def foo(a=[]): a.append(5) return a Python新手希望这个函数总是返回一个只包含一个元素的列表:[5].结果却非常不同,而 ...

  5. [python]函数默认参数顺序问题

    python 函数参数定义有四类: 1.必选参数:调用函数时候必须赋值的参数. a,须以正确的顺序传入函数b,调用时的数量必须和声明时的一样 def exa(x): return x #b作为参数进入 ...

  6. Python 函数(默认参数)

    默认参数 设置默认参数时,有两点需要注意:一是必选参数在前,默认参数在后,否则python的解释器会报错二是当函数有多个参数时,把变化大的参数放前面,变化小的放后面,变化小的参数就可以作为默认参数 d ...

  7. Python函数默认参数的陷阱

    默认参数实际上只有一个值 代码1 def func(l = 1): l += 1 print(l) func() func() func() 代码2 lst = [] def func(a,l = l ...

  8. python函数默认参数作用域

    当def函数参数默认值为对象时,例如列表[],字典{} 示例1:猜测一下,会输出什么??? def ddd(a,b=[]): b.append(a) return b print(ddd(1)) pr ...

  9. python函数默认参数坑

    def add(a=3,b): print a,b add(4) 这样写的话,运行的话就会报错:SyntaxError: non-default argument follows default ar ...

随机推荐

  1. Access数据类型和.NET数据类型映射

    下表列出了 Microsoft Access 和这些数据类型与 Microsoft.NET Framework 数据类型与 OleDbType 枚举的方式中使用的最常见的数据类型. 访问类型名称 数据 ...

  2. Salt: Master server cannot see any Minion

    Issue: When you set up a Salt Master server and several Minions, you may find that none of minions c ...

  3. 高通QSD MSM APQ区别

    高通msm是Mobile Station Modem 的缩写,即移动基带工作站,是指带有基带芯片的移动处理器,实际就是基带内置的手机处理器(soc)系列. qsd是qualcomm snapdrago ...

  4. 【OpenCV文档】用于角点检测的Fast算法

    原文地址:http://docs.opencv.org/trunk/doc/py_tutorials/py_feature2d/py_fast/py_fast.html#fast-algorithm- ...

  5. /sbin/insserv: No such file or directory

    /sbin/insserv: No such file or directory在Ubuntu下安装service服务,可能会报如下错误:/sbin/insserv: No such file or ...

  6. javascript之数组对象与数组常用方法

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. Hibernate与Spring的事务管理

    什么是事务 这个问题比较大,按照我的理解就是,一个事务内的n个操作,要么全部完成,一旦有一个操作有问题,那么所有的操作都全部回滚. Jdbc的事务 首先,大家已经知道了,事务说白了就是一个词----统 ...

  8. FreeMarker 生成Java、mybatis文件

    FreeMarker 生成Java.mybatis文件 将mysql数据库表通过FreeMarker生成对应的Java文件和对应的mybatis文件. FreeMarker是一款模板引擎: 即一种基于 ...

  9. AngularJS进阶(二十八)解决AngualrJS页面刷新导致异常显示问题

    解决AngualrJS页面刷新导致异常显示问题 绪 俗话说,细节决定成败,编程亦是如此.编程过程中我们可能会不自觉的忽视一些细节问题,殊不知,这些细节正是导致页面显示出现问题的地方.今略举一例,与君共 ...

  10. 取消选中单选框radio的三种方式

    作者: 铁锚 日期: 2013年12月21日 本文提供了三种取消选中radio的方式,代码示例如下: 本文依赖于jQuery,其中第一种,第二种方式是使用jQuery实现的,第三种方式是基于JS和DO ...