第二部分开始讨论Python的数据结构,这是Python的一个重点。Python的数据结构设计的很有特点:严谨、一致性好,使用灵活,操作方便。它用统一的风格处理序列数据,共用一套操作。

不同的脚本语言,往往设置不同的核心数据结构,围绕着这个核心组织数据,进行各种操作。其实仔细想一想,编程语言中基础的数据结构,是不是都可以归结为2类:序列数据/关联数据。序列可以用数组实现,关联可以用类似字典”Key-Value”的结构处理。抓住核心的概念,就容易对付了。而再细究下,关联数据也可以用数组实现。PHP就是。

PHP里面的核心数据结构是数组-Array,几乎一切数据处理都围绕着它运作。但是这个Array,其实包含了两个实体:数组和关联数组。JavaScript里面,就做了区分,关联数组用Object替代了。C里面就很原始,这些高级玩意儿统统没有。C++倒是有一套STL模版库,解决了不少问题。想要更高级的?自己做。相比之下,Python设计的就很严谨了。

Python把数组看成“序列”的一种,用这个概念涵盖。只要符合序列标准,就能执行序列的各种操作了。关联数组这些需求,推到字典对象里面去了。

Python的序列包括了更细腻的结构:list/tuple/deque/str/bytes等。list/deque/bytearray是可变的,可以任意操作里面的数据。tuple(元组)/str(字符串)不可变。字符串怎么还不可变?其实脚本基本都这么实现的。你修改字符串,就生成一个新的字符串给你,老的回收。字符串也算序列,这统一的概念,给了Python语言强大的操作能力。你看PHP操作字符串,只能用基础的类似C语言的函数。但是Python就能按序列,使用定义在序列中的各种方法来操作。

要学这部分内容,还是去看下“Learning Python 5th”吧,讲的比较细致,这本书面向中高级用户,入门的东西没有。第17页的图值得仔细观摩,它解释了序列数据结构的继承关系。

列表解析、生成器表达式,是Python设计的特殊的操作,算是一个特色。

列表解析例子:dummy = [ x for x in ‘abcd’ if x > 1] 其实就是个倒写的、简单的for循环,但是性能经过特别优化,比手写的for快。运算结果是个新的列表。

图灵出版了一本很薄的书《函数式编程思维》,里面对函数式编程的思想理念、方法做了简明扼要的阐述。里面提到函数式编程有3个基本单元:filter/map/reduce。其实列表解析就能在某些场合替代filter和map,性能还更高。

生成器表达式,从语法上说,就是方括号换成圆括号: [] -> (),但是意义差别很大。现代的语言,大多实现了“生成器”,这东西简单的说,就是推迟调用,需要时生成。在大数据量操作的时候有很大的优势 – 节省内存占用。

下面是元组 – tuple。其它书籍提到过,为何有了列表,还要有元组。这体现了设计者的严谨。另外,因为元组不可变,所以它能够作为字典对象的Key,而列表 – list就不能。

此节对元组提出了新的理解,作者认为,光把元组理解为不可变的列表是不够的。其实它还包含了其它的信息:元素的总数、位置信息,而通过这个信息,又可以方便的构建新的结构 – 具名元组(collections.namedtuple)。

元组的拆包操作语句是很巧妙的设计,很Pythonic。

city, year, pop = (‘Tokyo’, 2003, 32450)

一行语句就完成了多个变量的赋值。哪怕元组签套了其它元组,也可以正常的解析分配。

后面是“切片” – slice 操作,这个也很有Python特色。

“切片”是针对序列类型的数据对象的一个操作集合。什么意思?序列类型,其实就是可以像数组一样操作的对象。列表/字符串/元组都是序列类型。通过这种抽象,切片功能不再局限于数组本身,而是可以扩展到任意序列类型。对比另外的设计:字符串、数组都有各自不同的操作方法,这个模式要简单的多,而且一致性强,很有美感。

切片的一个基本形式:[a:b:c],数学上习惯用“左闭右开”的模式,即:操作上包括起点a,但是不包括终点b。

针对切片类型的各种操作,因为类型的一致,也可以直接使用了。C++里面的STL模版库,是个经典的设计案例。它完全遵循了“正交”的设计理念。算法+容器,可以独立替换、变化,而不影响代码操作。Python也有这种设计思想。

本章探讨了list.sort/sorted,以及bisect模块。

通用的容器,可能是操作最方便的,也同样是效率最低的。因为它没法针对某种情形进行优化。比如存储大量的浮点数,搜索特定的值等等。这里提到另外一个容器分类的概念:扁平序列、容器序列。扁平序列就是有针对性的,存储类型一致的容器。消耗资源小,速度快,但是无法自身签套。就是你不能在容器里面签套,同样类型的容器,而容器序列则可以:[1, [2, 3]]

如果你只操作数字类型、字符,不妨使用array.array库,专门优化。可以参考下:https://docs.python.org/3/library/array.html

这给我们提了醒:当操作的数据量很大的时候,先看看Python里是否有单独的、针对性优化的结构和容器,最后再考虑通用类型。这种优化效果可能非常的可观,而并不会增加多少开发量。

Python还有其它的库,用于数据操作,本章提到的:memoryview/NumPy/SciPy,以及队列。具体使用信息,可以根据线索,有针对性的查看文档说明。本章最后,有一些背景性质的讨论,值得一读。

一个小任务:用谷歌检索一下这个名字 – Tim Peters.

 

 

流畅的Python-Fluent Python读书笔记-03
流畅的Python-Fluent Python读书笔记-01