在Fluent Python一书第八章有一个示例,未看书以先很难理解这个示例运行的结果,我们先看结果,然后再分析问题原因:

定义了如下Bus类:

 class Bus:
def __init__(self, passengers=[]):
self.passengers = passengers def pick(self, name):
self.passengers.append(name) def drop(self, name):
self.passengers.remove(name)

创建两个Bus 类实例,bus1, bus2

>>> import Example8_12
>>> bus1 = Example8_12.Bus()
>>> bus2 = Example8_12.Bus()

假如bus1接到一个一名乘客Alice:

>>> bus1.pick('Alice')

此时我们看看bus2里的乘客:

>>> bus2.passengers
['Alice']

bus2本应该是空的,但是此时却有bus1 pick的乘客'Alice', 这是什么原因呢?

出现这个问题的根源是:默认值在定义函数时计算(通常是在import 模块时),并且默认值变成了函数对象的属性:

我们可以省察下类Bus __init__方法的属性

>>> dir(Example8_12.Bus.__init__)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

留意其中有个__defaults__属性,我们看看其中的内容:

>>> Example8_12.Bus.__init__.__defaults__
(['Alice'],)

我们可以看到'Alice'正在其中,这也是为什么bus2里会有乘客'Alice'的原因.

我们可以验证bus2.passengers是一个别名,它绑定到Bus1.__init__.__defaults__属性的第一个元素上了:

>>> Example8_12.Bus.__init__.__defaults__[0] is bus2.passengers
True

这个示例说明了为什么通常使用None作为接收可变值的参数的默认值。

这个类正确的书写应该如下:

 class Bus:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers) def pick(self, name):
self.passengers.append(name) def drop(self, name):
self.passengers.remove(name)

这里有一个原则,如果定义的函数接收可变参数,应该谨慎考虑调用方是否期望修改传入的参数。

Fluent Python: Mutable Types as Parameter Defaults: Bad Idea的更多相关文章

  1. 学习笔记之Fluent Python

    Fluent Python by Luciano Ramalho https://learning.oreilly.com/library/view/fluent-python/97814919462 ...

  2. 「Fluent Python」今年最佳技术书籍

    Fluent Python 读书手记 Python数据模型:特殊方法用来给整个语言模型特殊使用,一致性体现.如:__len__, __getitem__ AOP: zope.inteface 列表推导 ...

  3. Fluent Python: memoryview

    关于Python的memoryview内置类,搜索国内网站相关博客后发现对其解释都很简单, 我觉得学习一个新的知识点一般都要弄清楚两点: 1, 什么时候使用?(也就是能解决什么问题) 2,如何使用? ...

  4. python的types模块

    python的types模块 1.types是什么: types模块中包含python中各种常见的数据类型,如IntType(整型),FloatType(浮点型)等等. >>> im ...

  5. Python深入学习之《Fluent Python》 Part 1

    Python深入学习之<Fluent Python> Part 1 从上个周末开始看这本<流畅的蟒蛇>,技术是慢慢积累的,Python也是慢慢才能写得优雅(pythonic)的 ...

  6. Fluent Python: Classmethod vs Staticmethod

    Fluent Python一书9.4节比较了 Classmethod 和 Staticmethod 两个装饰器的区别: 给出的结论是一个非常有用(Classmethod), 一个不太有用(Static ...

  7. Fluent Python: @property

    Fluent Python 9.6节讲到hashable Class, 为了使Vector2d类可散列,有以下条件: (1)实现__hash__方法 (2)实现__eq__方法 (3)让Vector2 ...

  8. 《Fluent Python》---一个关于memoryview例子的理解过程

    近日,在阅读<Fluent Python>的第2.9.2节时,有一个关于内存视图的例子,当时看的一知半解,后来查了一些资料,现在总结一下,以备后续查询: 示例复述 添加了一些额外的代码,便 ...

  9. [Python] Pitfalls: About Default Parameter Values in Functions

    Today an interesting bug (pitfall) is found when I was trying debug someone's code. There is a funct ...

随机推荐

  1. iOS 获取蜂窝网络信号强度 包含iPhoneX XS XR XSMASX (最新)

    1.虽然各种直接获取信号强度的api都被封杀了.但是还有一个另类的黑魔法可以获取到.那就是遍历UIStatusBar了 网络上有的文章写的会崩溃 比如: - (int)getSignalStrengt ...

  2. 纯 js 让浏览器不缓存 ajax 请求

    开发「bufpay.com 个人即时到账收款平台」支付页面需要用到 ajax 轮询订单的支付状态. 现在浏览器对 ajax 的缓存策略遵循 http response header 里面的缓存设置,为 ...

  3. Linux中Kibana部署

    1.下载kibana安装包kibana-5.5.0-linux-x86_64.tar.gz tar –xzf kibana-5.5.0-linux-x86_64.tar.gz解压 把文件移动到 Mv ...

  4. vue-cli 3.0 使用axios配置跨域访问豆瓣接口

    vue-cli 3.0 配置axios跨域访问豆瓣接口 自己做的小demo 由于豆瓣api跨域问题,因此不能直接通过ajax请求访问,我们通过vue-cli提供给我们的代理 进行配置即可, 在根目录下 ...

  5. npm 取消代理 npm config delete proxy

    今天在安装electron时设置了代理,发现再npm install 安装别的总是装不上,只好取消代理. npm 取消代理 npm config delete proxy

  6. C/C++中的malloc、calloc和realloc

    1. malloc 原型:extern void *malloc(unsigned int num_bytes); 头文件:Visual C++6.0中可以用malloc.h或者stdlib.h 功能 ...

  7. Hadoop-Hive学习笔记(1)

    1. Hive什么 a.Hive是基于Hadoop的一个数据仓库工具(注意不是数据仓库),将结构化的数据文件映射成一张数据库表. b.Hive是SQL的解析引擎,可以把sql语句转换成MapReduc ...

  8. linux线程篇 (三) 线程的同步

    1 互斥量 pthreat_mutex_t mymutex; //1. 创建 初始化 int pthread_mutex_init(pthread_mutex_t *mutex, const pthr ...

  9. Java基础之instanceof和transient关键字用法

    instanceof 用于检测指定对象是否是某个类(本类.父类.子类.接口)的实例.Java中的instanceof也称为类型比较运算符,因为它将类型与实例进行比较. 返回true或false. 如果 ...

  10. java 获取目标时间到当前时间中间的月份和每月最大时间

    话不多说自己上代码: Date firstDate = DateUtil.formate("2018-01-01", "yyyy-MM-dd"); Date c ...