Python 用统一的风格去处理序列数据。不管是哪种数据结构,字符串、列表、字节序列、数组、XML 元素,抑或是数据库查询结果,它们都共用一套丰富的操作:迭代、切片、排序,还有拼接。
Python 标准库用 C 实现了丰富的序列类型,列举如下。
容器序列
list、tuple 和 collections.deque 这些序列能存放不同类型的数据。
扁平序列
str、bytes、bytearray、memoryview 和 array.array,这类序列只能容纳一种类型。
容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是
引用。换句话说,扁平序列其实是一段连续的内存空间。由此可见扁平序列其实更加紧
凑,但是它里面只能存放诸如字符、字节和数值这种基础类型。
序列类型还能按照能否被修改来分类。
下图显示了可变序列(MutableSequence)和不可变序列(Sequence)的差异,同时也
能看出前者从后者那里继承了一些方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VCjTF0vM-1612281306648)(D:\Note\FluentPython\img\2.1.PNG)]
列表推导可以帮助我们把一个序列或是其他可迭代类型中的元素过滤或是加工,然后再新建一个列表。
列表推导式:
l = [function(i) for i in iterable]
生成器推导式:
t = (function(i) for i in iterable)
# 用生成器表达式初始化元组和数组
>>> symbols = '$¢£¥€¤'
>>> tuple(ord(symbol) for symbol in symbols)
(36, 162, 163, 165, 8364, 164)
>>> import array
>>> array.array('I', (ord(symbol) for symbol in symbols))
array('I', [36, 162, 163, 165, 8364, 164])
列表推导、生成器表达式,以及同它们很相似的集合(set)推导和字典(dict)推导,在 Python 3 中都有了自己的局部作用域,就像函数似的。表达式内部的变量和赋值只在局部起作用,表达式的上下文里的同名变量还可以被正常引用,局部变量并不会影响到它们。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U7FmhcaK-1612281306651)(D:\Note\FluentPython\img\2.2.PNG)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXVheDOB-1612281306652)(D:\Note\FluentPython\img\2.3.PNG)]
元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段的数据,外加这个
字段的位置。正是这个位置信息给数据赋予了意义。
如果只把元组理解为不可变的列表,那其他信息——它所含有的元素的总数和它们的位
置——似乎就变得可有可无。但是如果把元组当作一些字段的集合,那么数量和位置信息
就变得非常重要了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BEIJFZyX-1612281306654)(D:\Note\FluentPython\img\2.4.PNG)]
两种常见形式:
a,b,_ = (1,2,'years')
b, a = a, b
运算符*****把一个可迭代对象拆开作为函数的参数:
>>> divmod(20, 8)
(2, 4)
>>> t = (20, 8)
>>> divmod(*t)
(2, 4)
>>> quotient, remainder = divmod(*t)
>>> quotient, remainder
(2, 4)
用*来处理剩下的元素,在 Python 中,函数用 *args
来获取不确定数量的参数算是一种经典写法了:
>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
>>> a, b, *rest = range(3)
>>> a, b, rest
(0, 1, [2])
>>> a, b, *rest = range(2)
>>> a, b, rest
(0, 1, [])
在平行赋值中,* 前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置
collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元组和一个有
名字的类——这个带名字的类对调试程序有很大帮助。
用 namedtuple 构建的类的实例所消耗的内存跟元组是一样的,因为字段名都被存在对应的类里面。这个实例跟普通的对象实例比起来也要小一些,因为Python 不会用 dict 来存放这些实例的属性。
>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates')
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
>>> tokyo
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722,
139.691667))
>>> tokyo.population
36.933
>>> tokyo.coordinates
(35.689722, 139.691667)
>>> tokyo[1]
'JP'
>>> City._fields
('name', 'country', 'population', 'coordinates')
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
>>> delhi = City._make(delhi_data)
>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population',
21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
>>> for key, value in delhi._asdict().items():
print(key + ':', value)
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)
除了从普通元组那里继承来的属性之外,具名元组还有一些自己专有的属性,几个最有用的:_fields 类属性、类方法_make(iterable)
和实例方法 _asdict()
[start:stop:step]
对 seq[start:stop:step]
进 行 求 值 的 时 候,Python 会 调 用 seq.__getitem__(slice(start, stop, step))
。
在 Python 里,像列表(list)、元组(tuple)和字符串(str)这类序列类型都支持切片操作,但是实际上切片操作比人们所想象的要强大很多。
my_ list[:x]
和 my_list[x:]
就可以了如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代对象。即便只有单独一个值,也要把它转换成可迭代的序列。
y_list[x:]` 就可以了
如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代对象。即便只有单独
一个值,也要把它转换成可迭代的序列。