Contents
  1. 1. 0x00 好久不见
  2. 2. 0x01 怎么爽了
  3. 3. 0x02 一般准则
    1. 3.1. Array
    2. 3.2. Tuple & named Tuple
    3. 3.3. Deque
  4. 4. 0x03 维护一个有序序列

0x00 好久不见

转眼间已经放假了。真的是好久没写了,书也是好久没看。这学期除了在忙活让我五味杂陈的“人脸识别”的项目,还有最后坑爹的压力表设计。但是好歹结果不错,都让我学到了很多。

以上是题外话,今天要说的可迭代对象 (iterable) 的一般用法。以往我们(也就是我啦)经常滥用 list 对象,在我的程序里几乎不是 list 就是 dict 。因为它们实在是太好用了,也是它们让我觉得写 python 非常爽。那么它们到底应该怎么用呐?

0x01 怎么爽了

当然是强大的列表表达式 (listcomps) 啦:

1
2
3
4
>>> symbols = '$¢£¥€¤'
>>> beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
>>> beyond_ascii
[162, 163, 165, 8364, 164]

用 listcomps 可以基本取代 map 函数,而且比它更方便、易读。如果把最外层的 []换成 () 就变成了生成器对象,这在要用计算好的对象做迭代的时候可以节约资源。

另外自然还有 dictcomps ,用来生成字典:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> DIAL_CODES = [
... (86, 'China'),
... (91, 'India'),
... (1, 'United States'),
... (62, 'Indonesia'),
... (55, 'Brazil'),
... (92, 'Pakistan'),
... (880, 'Bangladesh'),
... (234, 'Nigeria'),
... (7, 'Russia'),
... (81, 'Japan'),
... ]
>>> country_code = {country: code for code, country in DIAL_CODES}
>>> country_code
{'China': 86, 'India': 91, 'Bangladesh': 880, 'United States': 1,
'Pakistan': 92, 'Japan': 81, 'Russia': 7, 'Brazil': 55, 'Nigeria':
234, 'Indonesia': 62}

以上操作可以方便的把 dict 键值对交换。

0x02 一般准则

既然不能什么事都交给 list 干,那么应该遵循怎样的规则呢?下面给出了几种常用的对象:

  • list
  • array
  • tuple
  • deque
  • named tuple

这里面 array 是个比较特殊的对象,它是一个只能包含单一数据类型的序列 (flat sequence),其余的都可以包含各种各样的数据类型。另外,tuple 和 namedtuple 都是不可变对象,意味着它们一旦被赋值,是不允许改变的。

Array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> from array import array
>>> from random import random
>>> floats = array('d', (random() for i in range(10**7)))
>>> floats[-1]
0.07802343889111107
>>> fp = open('floats.bin', 'wb')
>>> floats.tofile(fp)
>>> fp.close()
>>> floats2 = array('d')
>>> fp = open('floats.bin', 'rb')
>>> floats2.fromfile(fp, 10**7)
>>> fp.close()
>>> floats2[-1]
0.07802343889111107

Array 主要用于储存大量的同类型数据,在这方面它比 list 要更快、更节约资源。不过我们一般用 numpy.array 。矩阵运算必不可少的工具。

Tuple & named Tuple

Tuples Are Not Just Immutable Lists

——书第26页

tuple 不仅仅是不可变的 list 。事实上,我们经常用 tuple 做记录 (record) 。

1
2
3
4
5
6
7
8
>>> traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'),
... ('ESP', 'XDA205856')]
>>> for passport in sorted(traveler_ids):
... print('%s/%s' % passport)
...
BRA/CE342567
ESP/XDA205856
USA/31195855

像这样的列表的元组 (list of tuples) 是十分常用的一种结构,它不仅仅储存记录,还可以十分方便的转换为 dict 对象。

namedtupletuple 的加强版:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates') #[1]
>>> 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'

[1]: Two parameters are required to create a named tuple: a class name and a list of field names, which can be given as an iterable of strings or as a single spacedelimited string.

Deque

deque (double-ended queue) 是一种双向队列,我用得比较少。

The class collections.deque is a thread-safe double-ended queue designed for fast inserting and removing from both ends.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> from collections import deque
>>> dq = deque(range(10), maxlen=10)
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3)
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
>>> dq.rotate(-4)
>>> dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
>>> dq.appendleft(-1)
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.extend([11, 22, 33])
>>> dq
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
>>> dq.extendleft([10, 20, 30, 40])
>>> dq
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)

0x03 维护一个有序序列

The bisect module offers two main functions—bisect and insort—that use the binary search algorithm to quickly find and insert items in any sorted sequence.

一旦我们有一个有序的序列,最好不要破坏它的有序性。 bisect 使用二分法来维护一个有序序列。

下面有一个通过这种方式来给成绩分级的巧妙例子:

1
2
3
4
5
6
7
>>> import bisect
>>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
... i = bisect.bisect(breakpoints, score)
... return grades[i]
...
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']

值得注意的是:grade 函数定义中的 breakpoints=[60, 70, 80, 90] 不是很稳妥,尽量不要用可变对象作为函数的默认参数。

其实 bisect 还有很多值得关注的函数和用法,具体可以参阅官方文档。

Contents
  1. 1. 0x00 好久不见
  2. 2. 0x01 怎么爽了
  3. 3. 0x02 一般准则
    1. 3.1. Array
    2. 3.2. Tuple & named Tuple
    3. 3.3. Deque
  4. 4. 0x03 维护一个有序序列