容器序列:list、tuple和collections.deque这些序列能存放不同类型的数据。
扁平序列:str、bytes、bytearray、memoryview和array.array,这些序列只能容纳一种类型。
可变序列:list、bytearray、array.array、collections.deque和memoryview。
不可变序列:tuple、str和bytes。
2-1 把字符串变成Unicode码位的列表
- # 方法一
- sysmbols = '$¥&*#@!'
- codes = []
- for symbol in sysmbols:
- codes.append(ord(symbol))
- print(codes)
-
- # 方法二
- sysmbols = '$¥&*#@!'
- codes = [ord(symbol) for symbol in sysmbols]
- print(codes)
-
- # 结果
- [36, 65509, 38, 42, 35, 64, 33]
- [36, 65509, 38, 42, 35, 64, 33]
句法提示:python会忽略代码里[]、{}、()中的换行,因此如果你的代码里有多行的列表、列表推导、生成器表达式、字典这一类的,可以省略不太好看的续行符\。
2-2 列表推导不会再有变量泄露的问题
- # 列表推导、生成器表达式、集合推导、字典推导,在python3中有自己局部作用域,不会影响同名变量。
- x = 'ABC'
- dummy = [ord(x) for x in x]
- print(x)
- print(dummy)
-
- # 结果
- ABC
- [65, 66, 67]
2-3 列表推导和map/filter组合结果相同,明显列表推导更简单
- sysmbols = '$¥&*#@!'
- beyond_ascii = [ord(s) for s in sysmbols if ord(s) > 127]
- print(beyond_ascii)
-
- beyond_ascii2 = list(filter(lambda c: c > 127, map(ord, sysmbols)))
- print(beyond_ascii2)
-
- # 结果
- [65509]
- [65509]
2-4 使用列表推导计算笛卡儿积
- colors = ['black', 'white']
- sizes = ['S', 'M', 'L']
- tshirts = [(color, size) for size in sizes
- for color in colors]
- print(tshirts)
-
- # 结果
- [('black', 'S'), ('white', 'S'), ('black', 'M'), ('white', 'M'), ('black', 'L'), ('white', 'L')]
列表推导的作用只有一个:生成列表
2-5 用生成器表达式初始化元组和数组
- sysmbols = '$¥&*#@!'
- tup = tuple(ord(symbol) for symbol in sysmbols)
- print(tup)
-
- import array
- arr = array.array('I', (ord(symbol) for symbol in sysmbols))
- print(arr)
-
- # 结果
- (36, 65509, 38, 42, 35, 64, 33)
- array('I', [36, 65509, 38, 42, 35, 64, 33])
2-6 使用生成器表达式计算笛卡儿积
生成器表达式逐个产出元素,不会一次性产出所有列表,避免额外的内存占用。
- colors = ['black', 'white']
- sizes = ['S', 'M', 'L']
- for tshirt in ('%s, %s,' %(c, s) for c in colors for s in sizes):
- print(tshirt)
-
- # 结果
- black, S,
- black, M,
- black, L,
- white, S,
- white, M,
- white, L,
2-7 把元组用作记录
- lax_coordinates = (33.9425, -118.408056)
- city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8041)
- traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]
- for passport in sorted(traveler_ids): #迭代过程中,passport变量被绑定到每个元组上
- print('%s/%s' %passport)
- for country, _ in traveler_ids: #拆包(unpacking),元组中第二个元组没用,用占位符“_”
- print(country)
-
- # 结果
- BRA/CE342567
- ESP/XDA205856
- USA/31195855
- USA
- BRA
- ESP
- lax_coordinates = (33.9425, -118.408056)
- latitude, longitude = lax_coordinates # 元组拆包
- print(latitude, longitude)
-
- dm = divmod(20, 8)
- print(dm)
-
- t = (20, 8)
- dm2 = divmod(*t) # 用*运算符把一个可迭代对象拆开作为函数的参数
- print(dm2)
-
- quotient, remainder = divmod(*t)
- print(quotient, remainder)
-
- # 结果
- 33.9425 -118.408056
- (2, 4)
- (2, 4)
- 2 4
os.path.split()函数会返回以路径和最后一个文件名组成的元组(path, last_part)
- import os
- path, filename = os.path.split('D:\pythonProject\Fluent_Python\Data_Structure\\array\\tuple.py')
- print(path)
- print(filename)
-
- # 结果
- D:\pythonProject\Fluent_Python\Data_Structure\array
- tuple.py
用 * 来处理剩下的元素
- a, b, *rest = range(5)
- print(a, b, rest)
- a, b, *rest = range(3)
- print(a, b, rest)
- a, b, *rest = range(2)
- print(a, b, rest)
-
- a, *body, c, d = range(5) # 在平行赋值中,*前缀只能用在一个变量名前,但是这个变量可以出现在复制表达式的任意位置
- print(a, body, c, d)
- *head, b, c, d = range(5)
- print(head, b, c, d)
-
- # 结果
- 0 1 [2, 3, 4]
- 0 1 [2]
- 0 1 []
- 0 [1, 2] 3 4
- [0, 1] 2 3 4
2-8 用嵌套元组来获取经度
- metro_areas = [
- ('Tokyo', 'JP', 36.933, (350689722, 139.691667)), # 每个元组有4个元素,其中最后一个元素时一对坐标
- ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
- ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
- ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
- ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
- ]
- print('{:15} | {:^9} | {:^9}'.format('city', 'lat.', 'long.'))
- fmt = '{:15} | {:9.4f} | {:9.4f}'
- for name, cc, pop, (latitude, longitude) in metro_areas: # 把最后一个元素拆包到由变量构成的元组里,获取坐标
- if longitude <= 0: # 输出限制在西半球
- print(fmt.format(name, latitude, longitude))
-
- # 结果
- city | lat. | long.
- Mexico City | 19.4333 | -99.1333
- New York-Newark | 40.8086 | -74.0204
- Sao Paulo | -23.5478 | -46.6358
2-9 定义和使用具名元组
- from collections import namedtuple
-
- City = namedtuple('City', 'name country population coordinates') # 创建一个具名元组需要两个参数,一个类名,一个类的各个字段的名字
- tokyo = City('Tokyo', 'JP', 36.933, coordinates=(35.689722, 139.691667))
- print(tokyo.population)
- print(tokyo.coordinates)
- print(tokyo[1])
-
- #结果
- 36.933
- (35.689722, 139.691667)
- JP
2-10 具名元组的属性和方法(接续2-9)
- print(City._fields) # _fields属性时一个包含这个类所有字段名称的元组
- Latlong = namedtuple('Latlong', 'lat long')
- delhi_data = ('Delhi NCR', 'IN', 21.935, Latlong(28.613889, 77.208889))
- delhi = City._make(delhi_data) # 用_make()通过接受一个可迭代对象来生成这个类的一个实例,它的作用跟City(*delhi_data)一样
- print(delhi._asdict()) # _asdict()把具名元组以collections.OrderedDict的形式返回,可以用它把元组里的信息友好的呈现
- for key, value in delhi._asdict().items():
- print(key + ':', value)
-
- #结果
- ('name', 'country', 'population', 'coordinates')
- {'name': 'Delhi NCR', 'country': 'IN', 'population': 21.935, 'coordinates': Latlong(lat=28.613889, long=77.208889)}
- name: Delhi NCR
- country: IN
- population: 21.935
- coordinates: Latlong(lat=28.613889, long=77.208889)
| 列表 | 元组 | ||
| s._add_(s2) | * | * | s + s2,拼接 |
| s._iadd_(s2) | * | s += s2,就地拼接 | |
| s.append(e) | * | 在尾部添加一个新元素 | |
| s.clear() | * | 删除所有元素 | |
| s._contains_(e) | * | * | s是否包含e |
| s.copy() | * | 列表的浅复制 | |
| s.count(e) | * | * | e在s中出现的次数 |
| s._delitem(p) | * | 把位于p的元素删除 | |
| s.extend(it) | * | 把可迭代对象it追加给s | |
| s._getitem_(p) | * | * | s[p],获取位置p的元素 |
| s._getnewargs_() | * | 在pickle中支持更加优化的序列化 | |
| s.index(e) | * | * | 在s中找到元素e第一次出现的位置 |
| s.insert(p,e) | * | 在位置p之前插入元素e | |
| s._iter_() | * | * | 获取s的迭代器 |
| s._len_() | * | * | len(s),元素的数量 |
| s._mul_(n) | * | * | s * n,n个s的重复拼接 |
| s._imul_(n) | * | s *= n,就地重复拼接 | |
| s._rmul_(n) | * | * | n * s,反向拼接 |
| s.pop([p]) | * | 删除最后或者是位于p的元素,并返回它的值 | |
| s.remove(e) | * | 删除s中的第一次出现的e | |
| s.reverse() | * | 就地把s的元素倒序排列 | |
| s._reversed_() | * | 返回s的倒序迭代器 | |
| s._setitem_(p, e) | * | s[p] = e,把元素e放在位置p,替代已经在那个位置的元素 | |
| s.sort([key], [reverse]) | * | 就地对s中的元素进行排序,可选的参数有键(key)和是否倒序 |
2-12 一个包含3个列表的列表,嵌套的3个列表各自有3个元素来代表井字游戏的一行方块。
- board = [['_'] * 3 for i in range(3)] #建立一个包含3个列表的列表,被包含的3个列表各自有3个元素
- print(board)
- board[1][2] = 'X' # 把第1行第2列的元素标记为X
- print(board)
-
- # 结果
- [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
- [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
2-13 含有3个指向同一对象的引用的列表是毫无用处的
- weird_board = [['_'] * 3] * 3 # 外面的列表其实包含3个指向同一个列表的引用,当不做修改的时候,看起来都还好
- print(weird_board)
- weird_board[1][2] = '0' # 试图标记第1行第2列的元素,就立马暴露列表内的3个引用指向同一个对象
- print(weird_board)
-
- # 结果
- [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
- [['_', '_', '0'], ['_', '_', '0'], ['_', '_', '0']]
例 *= 在可变和不可变序列上的作用
- l = [1, 2, 3]
- print(id(l)) # 列表最开始的id
- l *= 2
- print(id(l)) # 运用增量乘法后,列表的id没变,新元素追加到列表上
- t = (1, 2, 3)
- print(id(t)) # 元组最开始的id
- t *= 2
- print(id(t)) # 运用增量乘法后,新的元组被创建
-
- # 结果
- 2744821281408
- 2744821281408
- 2744824891520
- 2744821338496
注意:不要把可变对象放在元组里面。
2-14 一个谜题
- >>> t = (1,2,[30,40])
- >>> t[2]+=[50,60] # 因为tuple不支持对它的元素赋值,所以会抛出TypeError异常
- Traceback (most recent call last):
- File "
" , line 1, in - TypeError: 'tuple' object does not support item assignment
- >>> t
- (1, 2, [30, 40, 50, 60]) # t[2]被改动了
例 list.sort和sorted和它们的关键字参数
- fruits = ['grape', 'raspberry', 'apple', 'banana']
- print(sorted(fruits)) # 新建一个按照字母排序的字符串列表
- print(fruits) # 原列表没有变化
- print(sorted(fruits, reverse=True)) # 按照字母降序排列
- print(sorted(fruits, key=len)) # 新建一个按照长度排序的字符串列表
- print(sorted(fruits, key=len, reverse=True)) # 按照长度降序排列的结果
- print(fruits) # 原列表没有变化
- fruits.sort() # 对原列表就地排序,返回值None被控制台忽略
- print(fruits) # fruits本身被排序
-
- # 结果
- ['apple', 'banana', 'grape', 'raspberry']
- ['grape', 'raspberry', 'apple', 'banana']
- ['raspberry', 'grape', 'banana', 'apple']
- ['grape', 'apple', 'banana', 'raspberry']
- ['raspberry', 'banana', 'grape', 'apple']
- ['grape', 'raspberry', 'apple', 'banana']
- ['apple', 'banana', 'grape', 'raspberry']
bisect(haystack,needle)在haystack(干草堆)里搜索needle(针)的位置,需要满足的条件是,把needle插入这个位置后,haystack还能保持升序。也就是说这个函数返回的位置前面的值,都小于或等于needle的值。其中haystack必须是一个有序的序列。
2.17 在有序序列中用bisect查找某个元素的插入位置
- import bisect
- import sys
- HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
- NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]
- ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}'
- def demo(bisect_fn):
- for needle in reversed(NEEDLES):
- position = bisect_fn(HAYSTACK, needle) # 用特定的bisect函数来计算元素应该出现的位置
- offset = position * ' |' # 利用该位置来计算出需要几个分隔符号
- print(ROW_FMT.format(needle, position, offset)) # 把元素和其应该出现的位置打印出来
-
- if __name__ == '__main__':
- if sys.argv[-1] == 'left': #根据执行命令最后一个参数来选用bisect函数,例:python bisect_demo.python left
- bisect_fn = bisect.bisect_left
- else:
- bisect_fn = bisect.bisect
- print('DEMO:', bisect_fn.__name__) # 把选定的函数打印出来
- print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))
- demo(bisect_fn)
-
- # 结果
- DEMO: bisect_right
- haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30
- 31 @ 14 | | | | | | | | | | | | | |31
- 30 @ 14 | | | | | | | | | | | | | |30
- 29 @ 13 | | | | | | | | | | | | |29
- 23 @ 11 | | | | | | | | | | |23
- 22 @ 9 | | | | | | | | |22
- 10 @ 5 | | | | |10
- 8 @ 5 | | | | |8
- 5 @ 3 | | |5
- 2 @ 1 |2
- 1 @ 1 |1
- 0 @ 0 0
-
- # 执行python bisect_demo.python left结果
- DEMO: bisect_left
- haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30
- 31 @ 14 | | | | | | | | | | | | | |31
- 30 @ 13 | | | | | | | | | | | | |30
- 29 @ 12 | | | | | | | | | | | |29
- 23 @ 9 | | | | | | | | |23
- 22 @ 9 | | | | | | | | |22
- 10 @ 5 | | | | |10
- 8 @ 4 | | | |8
- 5 @ 2 | |5
- 2 @ 1 |2
- 1 @ 0 1
- 0 @ 0 0
2-18 根据一个分数,找到它所对应的成绩
- import bisect
-
- def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
- i = bisect.bisect(breakpoints, score)
- print(grades[i])
- if __name__ == '__main__':
- [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
-
- # 结果
- F
- A
- C
- C
- B
- A
- A
insort(seq, item)把变量item插入到序列seq中,并能保持seq的升序顺序。
2-19 insort可以保持有序序列的顺序
- import bisect
- import random
-
- SIZE = 7
- random.seed(1729)
- my_list = []
- for i in range(SIZE):
- new_item = random.randrange(SIZE * 2)
- bisect.insort(my_list, new_item)
- print('%2d ->' % new_item, my_list)
-
- # 结果
- 10 -> [10]
- 0 -> [0, 10]
- 6 -> [0, 6, 10]
- 8 -> [0, 6, 8, 10]
- 7 -> [0, 6, 7, 8, 10]
- 2 -> [0, 2, 6, 7, 8, 10]
- 10 -> [0, 2, 6, 7, 8, 10, 10]
2-20 一个浮点型数组的创建、存入文件和从文件读取的过程
- from array import array
- from random import random
-
- floats = array('d', (random() for i in range(10 ** 7))) # 利用一个可迭代对象生成器表达式来建立一个双精度浮点数组(类型码是’d')
- print(floats[-1]) # 查看数组的最后一个元素
- fp = open('floats.bin', 'wb')
- floats.tofile(fp) # 把数组存入一个二进制文件里
- fp.close()
- floats2 = array('d') # 新建一个双精度浮点空数组
- fp = open('floats.bin', 'rb')
- floats2.fromfile(fp, 10 ** 7) # 把1000万个浮点数从二进制文件里读取出来
- fp.close()
- print(floats2[-1]) # 查看新数组的最后一个元素
- print(floats2 == floats) # 检查两个数组的内容是不是完全一样
-
- # 结果
- 0.5099803693183845
- 0.5099803693183845
- True
| 列表 | 数组 | ||
| s.__add__(s2) | * | * | s + s2,拼接 |
| s.__iadd__(s2) | * | * | s += s2,就地拼接 |
| s.append(e) | * | * | 在尾部添加一个元素 |
| s.byteswap | * | 翻转数组内每个元素的字节序列,转换字节序 | |
| s.clear() | * | 删除所有元素 | |
| s.__contains__(e) | * | * | s是否含有e |
| s.copy() | * | 对列表浅复制 | |
| s.__copy() | * | 对copy.copy的支持 | |
| s.count(e) | * | * | s中e出现的次数 |
| s.__deepcopy__() | * | 对copy.deepcopy的支持 | |
| s.__delitem__(p) | * | * | 删除位置p的元素 |
| s.extend(it) | * | * | 将可迭代对象it里的元素添加到尾部 |
| s.frombytes(b) | * | 将压缩成机器值得字节序列读出来添加到尾部 | |
| s.fromfile(f,n) | * | 将二进制文件f内含有机器值读出来添加到尾部,最多添加n项 | |
| s.fromlist(l) | * | 将列表里的元素添加到尾部,如果其中任何一个元素导致了TypeError异常,那么所有的添加都会取消 | |
| s.__getitem__(p) | * | * | s[p],读取位置p得元素 |
| s.index(e) | * | * | 找到e在序列中第一次出现的位置 |
| s.insert(p,e) | * | * | 在位于p得元素之前插入元素e |
| s.itemsize | * | 数组中每个元素得长度是几个字节 | |
| s.__iter__() | * | * | 返回迭代器 |
| s.__len__() | * | * | len(s),序列的长度 |
| s.__mul__(n) | * | * | s * n,重复拼接 |
| s.__imul__(n) | * | * | s *= n,就地重复拼接 |
| s.__rmul__(n) | * | * | n * s,反向重复拼接* |
| s.pop([p]) | * | * | 删除位于p得值并返回这个值,p得默认值是一个元素的位置 |
| s.remove(e) | * | * | 删除序列里第一次出现的e元素 |
| s.reverse(e) | * | * | 就地调转序列中元素的位置 |
| s.__reverse__() | * | 返回一个从尾部开始扫描元素的迭代器 | |
| s.__setitem__(p,e) | * | * | s[p] = e,把位于p位置的元素替换成e |
| s.sort([key], [reverse]) | * | 就地排序序列,可选参数有key和reverse | |
| s.tobytes() | * | 把所有元素的机器值用bytes对象的形式返回 | |
| s.tofile(f) | * | 把所有元素以机器值得形式写入一个文件 | |
| s.tolist() | * | 把数组转换成列表,列表里的元素类型是数字对象 | |
| s.typecode | * | 返回只有一个字符的字符串,代表数组元素在C语言中得类型 |
2-21 通过改变数组中的一个字节来更新数组里某个元素的值。
- import array
-
- numbers = array.array('h', [-2, -1, 0, 1, 2]) # 利用含有5个短整型有符号整数的数组创建一个memoryview
- memv = memoryview(numbers)
- print(len(memv))
- print(memv[0]) # memv里的5个元素跟数组里的没有区别
- memv_oct = memv.cast('B') # 创建一个memv_oct,把memv里的内容转换成‘B’类型,无符号字符
- print(memv_oct.tolist()) # 以列表得形式查看memv_oct的内容,出现10个元素,可以研究存储知识,源码,补码
- memv_oct[5] = 4 # 把位于位置5的字节赋值成4
- print(numbers) # 因为把占2个字节的整数的高位字节改成4,所以这个有符号整数的值就变成了1024
-
- # 结果
- 5
- -2
- [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
- array('h', [-2, -1, 1024, 1, 2])
2-22 对numpy.ndarray的行和列进行基本操作
- import numpy
-
- a = numpy.arange(12) # 新建一个0~11的整数numpy.ndarray
- print(a)
- print(type(a))
- print(a.shape) # 数组的维度,一个一维的,有12个元素
- a.shape = 3, 4 # 把数组变成2维的
- print(a)
- print("------------")
- print(a[2]) # 打印出第2行
- print("----------")
- print(a[2, 1]) # 打印第2行第1列的元素
- print("----------")
- print(a[:, 1]) # 打印第1列
- print("----------")
- print(a.transpose()) # 把行和列交换,得到一个新数组
-
- # 结果
- [ 0 1 2 3 4 5 6 7 8 9 10 11]
- <class 'numpy.ndarray'>
- (12,)
- [[ 0 1 2 3]
- [ 4 5 6 7]
- [ 8 9 10 11]]
- ------------
- [ 8 9 10 11]
- ----------
- 9
- ----------
- [1 5 9]
- ----------
- [[ 0 4 8]
- [ 1 5 9]
- [ 2 6 10]
- [ 3 7 11]]
- from collections import deque
-
- dq = deque(range(10), maxlen=10) # maxlen是一个可选参数,代表这个队列可以容纳的元素的数量,一旦设定,这个属性就不能修改了
- print(dq)
- dq.rotate(3) # 队列的旋转操作接受一个n,当n>0时,队列的最右边的n个元素会被移动到队列的左边。当n<0时,最左边的n个元素会被移动到右边
- print(dq)
- dq.rotate(-4)
- print(dq)
- dq.appendleft(-1) # 当试图会一个已满的队列做头部添加操作的时候,它尾部的元素会被删除掉。注意在下一行里,元素0被删除了
- print(dq)
- dq.extend([11, 22, 33]) # 在尾部添加3个元素的操作会挤掉-1、1、2
- print(dq)
- dq.extendleft([10, 20, 30, 40]) # extendleft(iter)方法会把迭代器里的元素逐个添加到双向队列的左边,因此迭代器里的所有元素会逆序出现在队列里
- print(dq)
-
- # 结果
- deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
- deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
- deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
- deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
- deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
- deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)