你可能注意到了,之前提到的几个操作可以无差别地应用于文本、列表和表格上,我们把文本、列表和表格叫做数据火车,for命令通常能作用于数据火车上。—Geurts、Meertens和Pemberton, ABC Programmer’s Handbook
ABC语言很多点子在现在看起来都很有Python风格
Python从ABC继承了用统一的风格处理序列数据这一特点
第一种分类方法
第二种分类方法
列表推导:是构建列表的快捷方式,它异常强大,但相关语法比较晦涩
生成器表达式:具有生成各种类型的元素并用它们来填充序列的功能
列表推导:列表推导可以帮助我们把一个序列或是其他可迭代类型中的元素过滤或是加工,然后新建一个列表
比较使用列表推导和不使用列表推导代码的可读性
把一个字符串变成Unicode码位的列表
|
|
把字符串变成Unicode码位的另外一种写法
|
|
使用列表推导的原则
Python3中列表推导不存在变量泄漏的问题
|
|
举例:用列表推导和map/filter组合来创建同样的表单
|
|
|
|
总结
笛卡尔积
使用列表推导计算笛卡尔积
|
|
生成器表达式
生成器表达式语法与列表推导差不多,只不过把方括号换成了圆括号
如何用生成器表达式建立元组和数组
|
|
使用生成器表达式计算笛卡尔积
|
|
生成器表达式最大的优点:逐个产出元素,可以节省内存
元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段的数据,外加这个字段的位置,正是这个位置信息给数据赋予了意义
如果在任何的表达式里我们在元组内对元素排序,这些元素所携带的信息就会丢失,因为这些信息是跟它们的位置有关的
把元组用作记录
|
|
元组拆包举例
平行赋值:把一个可迭代对象中的元素,一并赋值到由对应的变量组成的元组中
|
|
不使用中间变量交换两个变量的值
|
|
用*
运算符把一个可迭代对象拆开作为函数的参数
|
|
使用元组拆包接收函数以元组形式返回的多个值
|
|
元组拆包注意
*
来表示忽略多余的元素用*
处理剩下的元素
|
|
在平行赋值时,*前缀最多只能用于一个变量,但是这个变量可以出现在赋值表达式的任意位置
|
|
接受表达式的元组可以是嵌套式的,例如(a, b, (c, d))
只要这个接受元组的嵌套结构符合表达式本身的嵌套结构,Python就可以做出正确的对应
嵌套元组Demo
|
|
注意
def fn(a, (b, c), d):
collections.namedtuple
是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类
namedtuple
创建的类的大小
__dict__
来存放这些实例的属性用具名元组来记录一个城市的信息
|
|
具名元组还有一些自己专有的属性
_fields
类属性、类方法_make(iterable)
、实例方法_asdict()
|
|
在切片和区间操作里不包含区间范围的最后一个元素是Python的风格
切片和区间忽略最后一个元素的好处
一个众所周知的秘密是,我们还可以用s[a:b:c]的形式对s在a和b之间以c为间隔取值
|
|
可以使用slice()
数据类型声明切片变量,这样可以增加代码可读性
[]运算符还可以使用以逗号分开的多个索引或者是切片
numpy.ndarray
就可以用a[i, j]
这种形式来获取二维矩阵中的元素a[m:n, k:l]
的方式来得到二维切片Python内置的序列类型都是一维的,因此它们只支持单一的索引,成对出现的索引大部分在外部库中使用,例如Numpy等
如果把切片放在赋值语句的左边,或者把它作为del操作的对象,我们就可以对序列进行嫁接、切除或就地修改操作
注意:如果赋值的对象是一个切片的话,那么赋值语句的右侧必须是一个可迭代对象,即便只有单独一个值,也要转换成可迭代序列
例如
|
|
Python程序员会默认序列是支持+
和*
操作的
通常+
号两侧的序列由相同类型的数据所构成
在拼接过程中,两个被操作的序列都不会被修改,Python会新建一个包含同样类型数据的序列来作为拼接的结果
如果想把一个序列复制几份然后再拼接起来,更快捷的做法是把这个序列乘以一个整数,同样这个操作也会产生一个新序列
|
|
方案一:使用列表推导
|
|
方案二:一种错的方法,此时列表中嵌套的三个子列表全部指向同一对象
|
|
方案三:等价于方案一,只是不够简洁
|
|
方案四:等价于方案二,所以也是错误的
|
|
增量赋值运算符 +=
和 *=
的表现取决于它们的第一个操作对象
+=背后的特殊方法是__iadd__
(用于’就地加法’),工作原理如下
a += b
__iadd__
方法,就调用这个方法a.extend(b)
一样__iadd__
方法,a += b
这个表达式的效果就变得与a = a + b
一样了
a + b
,得到一个新的对象a
总体来说,可变序列一般都实现了__iadd__
方法,而不可变序列根本不支持这个操作,对这个方法的实现也无从谈起
但是不可变序列是支持 +=
运算符的,它只是把这个运算当作a = a + b
处理
|
|
list.sort
list.sort
方法会就地排序列表,也就是说不会把原列表复制一份None
,提醒你本方法不会新建一个列表None
其实是Python的一个惯例
None
sorted
sorted
方法会新建一个列表作为返回值sorted
接受的是什么参数,都将返回一个列表list.sort
和sorted
方法都有两个可选的关键字参数
reserve
True
,被排序的序列里的元素都会以降序输出key
sortDemo
|
|
标准库的bisect模块给我们提供了二分查找算法
bisect模块包含两个主要函数,bisect
和insort
,两个函数都利用二分查找算法来在有序序列中查找或插入元素
简介
bisect(haystack, needle)
在haystack(干草垛)里搜索needle(针)的位置bisect
的可选参数
lo
:表示搜索范围的下标,包括这个元素(老规矩),默认为0
hi
:表示搜索范围的上标,不包括这个元素(老规矩),默认为len()
bisect_left
和bisect_right
bisect
函数其实就是bisect_right
函数bisect_left
返回的插入位置是跟它相等的元素之前的位置bisect_right
返回的插入位置是跟它相等的元素之后的位置demo:根据一个分数,找到它所对应的成绩
|
|
insort(seq, item)
把变量item插入到序列seq中,并能保持seq的升序排序
insort
和bisect
一样,有lo
和hi
两个可选参数用来控制查找的范围
它也有个变体叫insort_left
比如,要存放1000万个浮点数的话,数组(array)要高效得多,因为数组在背后存放的并不是float对象,而是数字的机器翻译,也就是字节描述
再比如,如果需要频繁对序列做先进先出的操作,deque(双端队列)的速度应该会更快
如果在你的代码里,包含操作的频率很高,用set(集合)会更合适,因为set是无序的
简介
array.array
比list
是更好的选择.pop
、.insert
、.extend
.frombytes
和.tofile
创建数组需要一个类型码,这个类型码用来表示在底层的C语言应该存放怎样的数据类型
Python不会允许你在数组里存放除指定数据类型之外的数据
从Python3.4开始,数组(array)类型不再支持诸如list.sort()
这种就地排序方法
arrayDemo
|
|
memoryview
是一个内置类,它能让用户在不需要复制内容的前提下,在数据结构之间共享内存
其中memoryview.cast
可以用不同的方式读写同一块内存数据,而且内容字节不会随意移动
memoryviewDemo
|
|
Numpy实现了多维同质数组(homogeneous array)和矩阵
Scipy是基于Numpy的另一个库,提供了很多跟科学计算有关的算法,专为线性代数、数值分析和统计学而设计
Scipy把基于C和Fortran的工业级数学计算功能用交互式且高度抽象的Python包装起来,让科学家如鱼得水
NumpyDemo
|
|
利用.append和.pop方法,我们可以把列表当作栈或者队列使用,但是删除列表的第一个元素之类的操作是很费时的
collections.deque
类(双向队列)是个线程安全、可以快速从两端添加或者删除元素的数据类型
append
和popleft
都是原子操作,也就是deque可以在多线程程序中安全地当作先进先出的队列使用,而使用者不必担心资源锁问题
dequeDemo
|
|
Python序列类型最常见的分类为可变和不可变序列,另外一种分类方式也很有用,那就是把它们分成扁平序列和容器序列
列表推导和生成器表达式则提供了灵活构建和初始化序列的方式
元组在Python中扮演两种角色
序列切片
重复拼接seq * n
在正确使用的前提下,能让我们方便地初始化含有不可变元素的多维列表
增量赋值 +=
和 *=
会区别对待可变和不可变序列
序列的sort方法和内置的sorted函数可以实现就地排序和新建序列排序
bisect.bisect实现了二分查找
array.array、deque、Numpy等提供了除了列表之外的其他选择