这章讨论的是字典和集合。其实前面我提到过:编程语言中基础的数据结构,都可以归结为2类:序列数据/关联数据。字典就是关联数据类型,它在Python的内部实现中也是大量应用。
注意开头提到的一些内容,它体现了Python设计的基本思维。定义各类属性(迭代/包含检查/尺寸…)-> 实现这些属性的接口 -> 组合成对象 -> 对象间的相互调用(鸭子类型)。这种模式保证了语言的灵活性,又不失优雅和可靠性。
比如,能做dict的key值的数据,必须是可以哈希(散列)-hash的类型。那什么是可以散列的呢?必须满足规定的几个属性特征才行:有__hash__方法,有__qe__比较方法. 如果检索dict对象,找不到会发生什么?语言定义了__missing__方法,跟一个框架一般,只要你实现就能在挂钩的地方拦截住。不过要留神:这些魔术方法不能乱用,特别是在魔术方法内,不能滥用其它魔术方法。因为一个方法内部可能调用了其它方法,有可能导致死循环。使用的时候要仔细查阅文档。
字典推导是什么?就跟列表推导近似,列表推导生成列表,字典推导生成字典。几个计算符号要区别:
{…} -字典推导
[…] – 列表推导
(…) – 生成器表达式
这本书讲的最多的,不是常规对象的用法 – 那是入门书干的事情。它讲的最多的是常用的扩展用法/高级手段。
操作字典的时候,常见的操作是处理找不到key的情况,大家都知道可以用d.get(k, default)来预防,但是这并不是最佳手段。实际可以根据需要,使用setdefault指令简化操作。
Python的collections里面收集了一些字典库的额外扩展,可以查看下,这样在需要时候就能拿来用。相关链接在这里:
https://docs.python.org/3.5/library/collections.html
出于运行效率的考虑,Python对一些常用的操作类型做了内部实现,极大的提升了性能 – 因为那是C的速度。比如集合操作。
这里的关键在于,你作为用户,要有这个意识,对Python熟悉,知道要完成的这个任务,可以用Python内置的数据类型直接、转化解决。
比如求两个数据列表的公共部分,一些人的直觉思路是用循环判断,其实用set快得多。
commonData = set(oneList) & set(twoList)
集合的字面量是字典的“一半”:{2,3,4} ,没有映射部分。相应的,集合也有个推导操作:
{ i for i in range(1, 20) } #还是没有另一半的映射部分
本章最后,探讨的是dict/set内部实现机制,以及这些设计带来的优点和弱点。
dict内部使用hash机制,映射检索速度是常数O(1),但是这是用空间换时间,它消化的内存比较大。
从语言设计角度说,Python确实考虑的周到细致,充分体现了“简单而正确”的准则。我读过的那本《UNIX编程艺术》里面提到了很多设计原则和模式,可谓技术界的精华。Python在设计思路上做到了。
书中最后的杂谈也提到:PHP的数组,混合实现了列表和字典,这种设计就混淆了不同数据类型的特性以及设计上的考虑维度,是有缺陷的。JavaScript稍好一些,毕竟它的对象字面量其实就是个字典,它的array也比较灵活。
Python Cookbook 第三版第一章可以查阅一下,里面有很多数据结构的巧妙使用。