第一章:数据结构和算法

1 解压序列赋值给多个变量

问题:

现在有一个包含 N 个元素的元组或者是序列,怎样将它里面的值解压后同时赋值
给 N 个变量?

解决方案:

代码示例:

>>> p = (4, 5)
>>> x, y = p
>>> x
4
>>> y
5
>>>
>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> name, shares, price, date = data
>>> name
'ACME'
>>> date
(2012, 12, 21)
>>> name, shares, price, (year, mon, day) = data
6
《Python Cookbook》第三版, Release 2.0.0
>>> name
'ACME'
>>> year
2012
>>> mon
12
>>> day
21
>>>

如果变量个数和序列元素的个数不匹配,会产生一个异常。
代码示例:

>>> p = (4, 5)
>>> x, y, z = p
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack
>>>

总结

实际上,这种解压赋值可以用在任何可迭代对象上面:

  1. 列表

  2. 元组

  3. 字符串

  4. 文件对象

  5. 迭代器

  6. 生成器

代码示例:

>>> s = 'Hello'
>>> a, b, c, d, e = s
>>> a
'H'
>>> b
'e'
>>> e
'o'
>>>

有时候,你可能只想解压一部分,丢弃其他的值。对于这种情况 Python 并没有提
供特殊的语法。

但是你可以使用任意变量名去占位,到时候丢掉这些变量就行了。
代码示例:

>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> _, shares, price, _ = data
>>> shares
50
>>> price
91.1
>>>

你必须保证你选用的那些占位变量名在其他地方没被使用到。

2 解压可迭代对象赋值给多个变量

问题:

如果一个可迭代对象的元素个数超过变量个数时,会抛出一个 ValueError 。那么
怎样才能从这个可迭代对象中解压出 N 个元素出来?

解决方案

Python 的星号表达式可以用来解决这个问题。比如,你在学习一门课程,在学期
末的时候,你想统计下家庭作业的平均成绩,但是排除掉第一个和最后一个分数。如
果只有四个分数,你可能就直接去简单的手动赋值,但如果有 24 个呢?这时候星号表
达式就派上用场了:

def drop_first_last(grades):
first, *middle, last = grades
return avg(middle)

另外一种情况,假设你现在有一些用户的记录列表,每条记录包含一个名字、邮
件,接着就是不确定数量的电话号码。你可以像下面这样分解这些记录:

>>> record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> phone_numbers
['773-555-1212', '847-555-1212']
>>>

值得注意的是上面解压出的 phone numbers 变量永远都是列表类型,不管解压的
电话号码数量是多少 (包括 0 个)。所以,任何使用到 phone numbers 变量的代码就不
需要做多余的类型检查去确认它是否是列表类型了。
星号表达式也能用在列表的开始部分。比如,你有一个公司前 8 个月销售数据的序
列,但是你想看下最近一个月数据和前面 7 个月的平均值的对比。你可以这样做:

*trailing_qtrs, current_qtr = sales_record
trailing_avg = sum(trailing_qtrs) / len(trailing_qtrs)
return avg_comparison(trailing_avg, current_qtr)

下面是在 Python 解释器中执行的结果:

>>> *trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
>>> trailing
[10, 8, 7, 1, 9, 5, 10]
>>> current
3

讨论

扩展的迭代解压语法是专门为解压不确定个数或任意个数元素的可迭代对象而设计
的。通常,这些可迭代对象的元素结构有确定的规则(比如第 1 个元素后面都是电话
号码),星号表达式让开发人员可以很容易的利用这些规则来解压出元素来。而不是通
过一些比较复杂的手段去获取这些关联的的元素值。
值得注意的是,星号表达式在迭代元素为可变长元组的序列时是很有用的。比如,
下面是一个带有标签的元组序列:

records = [
('foo', 1, 2),
('bar', 'hello'),
('foo', 3, 4),
]
def do_foo(x, y):
print('foo', x, y)
def do_bar(s):
print('bar', s)
for tag, *args in records:
if tag == 'foo':
do_foo(*args)
elif tag == 'bar':
do_bar(*args)

星号解压语法在字符串操作的时候也会很有用,比如字符串的分割
代码示例:

>>> line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
>>> uname, *fields, homedir, sh = line.split(':')
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'
>>>

有时候,你想解压一些元素后丢弃它们,你不能简单就使用 * ,但是你可以使用一
个普通的废弃名称,比如_ 或者 ign 。
代码示例:

>>> record = ('ACME', 50, 123.45, (12, 18, 2012))
>>> name, *_, (*_, year) = record
>>> name
'ACME'
>>> year
2012
>>>

在很多函数式语言中,星号解压语法跟列表处理有许多相似之处。比如,如果你有
一个列表,你可以很容易的将它分割成前后两部分:

>>> items = [1, 10, 7, 4, 5, 9]
>>> head, *tail = items
>>> head
1
>>> tail
[10, 7, 4, 5, 9]
>>>

如果你够聪明的话,还能用这种分割语法去巧妙的实现递归算法。比如:

>>> def sum(items):
... head, *tail = items
... return head + sum(tail) if tail else head
...
>>> sum(items)
36
>>>

然后,由于语言层面的限制,递归并不是 Python 擅长的。因此,最后那个递归演
示仅仅是个好奇的探索罢了,对这个不要太认真了。

3 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

4 查找最大或最小的 N 个元素

问题:

怎样从一个集合中获得最大或者最小的 N 个元素列表?

解决方案:

heapq 模块有两个函数:nlargest() 和 nsmallest() 可以完美解决这个问题。

import heapq
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]

两个函数都能接受一个关键字参数,用于更复杂的数据结构中:

portfolio = [
{'name': 'IBM', 'shares': 100, 'price': 91.1},
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
{'name': 'FB', 'shares': 200, 'price': 21.09},
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
{'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])

译者注:上面代码在对每个元素进行对比的时候,会以 price 的值进行比较。

讨论

如果你想在一个集合中查找最小或最大的 N 个元素,并且 N 小于集合元素数量,
那么这些函数提供了很好的性能。因为在底层实现里面,首先会先将集合数据进行堆
排序后放入一个列表中:

>>> nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
>>> import heapq
>>> heapq.heapify(nums)
>>> nums
[-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]
>>>

堆数据结构最重要的特征是 heap[0] 永远是最小的元素。并且剩余的元素可以很
容易的通过调用 heapq.heappop() 方法得到,该方法会先将第一个元素弹出来,然后
用下一个最小的元素来取代被弹出元素 (这种操作时间复杂度仅仅是 O(log N),N 是
堆大小)。比如,如果想要查找最小的 3 个元素,你可以这样做:

>>> heapq.heappop(nums)
-4
>>> heapq.heappop(nums)
1
>>> heapq.heappop(nums)
2
  1. 当要查找的元素个数相对比较小的时候,函数 nlargest() 和 nsmallest() 是很合适的。
  2. 如果你仅仅想查找唯一的最小或最大 (N=1) 的元素的话,那么使用 min() 和max() 函数会更快些。
  3. 如果 N 的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点 ( sorted(items)[:N] 或者是 sorted(items)[-N:] )。
  4. 需要在正确场合使用函数 nlargest() 和 nsmallest() 才能发挥它们的优势 (如果N 快接近集合大小了,那么使用排序操作会更好些)。

尽管你没有必要一定使用这里的方法,但是堆数据结构的实现是一个很有趣并且值
得你深入学习的东西。基本上只要是数据结构和算法书籍里面都会有提及到。 heapq
模块的官方文档里面也详细的介绍了堆数据结构底层的实现细节。

 

5 实现一个优先级队列

 

问题:

 

怎样实现一个按优先级排序的队列?并且在这个队列上面每次 pop 操作总是返回
优先级最高的那个元素

 

解决方案:

 

下面的类利用 heapq 模块实现了一个简单的优先级队列

import heapq
class PriorityQueue:
def __init__(self):
self._queue = []
self._index = 0

def push(self, item, priority):
heapq.heappush(self._queue, (-priority, self._index, item))
self._index += 1
def pop(self):
return heapq.heappop(self._queue)[-1]

下面是它的使用方式:

>>> class Item:
... def __init__(self, name):
... self.name = name
... def __repr__(self):
... return 'Item({!r})'.format(self.name)
...
>>> q = PriorityQueue()
>>> q.push(Item('foo'), 1)
>>> q.push(Item('bar'), 5)
>>> q.push(Item('spam'), 4)
>>> q.push(Item('grok'), 1)
>>> q.pop()
Item('bar')
>>> q.pop()
Item('spam')
>>> q.pop()
Item('foo')
>>> q.pop()
Item('grok')
>>>

仔细观察可以发现,第一个 pop() 操作返回优先级最高的元素。另外注意到如果
两个有着相同优先级的元素 ( foo 和 grok ),pop 操作按照它们被插入到队列的顺序返
回的。

 

讨论

 

这 一 小 节 我 们 主 要 关 注 heapq 模 块 的 使 用。 函 数 heapq.heappush() 和
heapq.heappop() 分别在队列 queue 上插入和删除第一个元素,并且队列 queue 保证
第一个元素拥有最小优先级 (1.4 节已经讨论过这个问题)。 heappop() 函数总是返回”
最小的” 的元素,这就是保证队列 pop 操作返回正确元素的关键。另外,由于 push 和
pop 操作时间复杂度为 O(log N),其中 N 是堆的大小,因此就算是 N 很大的时候它们
运行速度也依旧很快。
在上面代码中,队列包含了一个 (-priority, index, item) 的元组。优先级为负
数的目的是使得元素按照优先级从高到低排序。这个跟普通的按优先级从低到高排序
的堆排序恰巧相反。
index 变量的作用是保证同等优先级元素的正确排序。通过保存一个不断增加的
index 下标变量,可以确保元素按照它们插入的顺序排序。而且, index 变量也在相
同优先级元素比较的时候起到重要作用。

为了阐明这些,先假定 Item 实例是不支持排序的:

>>> a = Item('foo')
>>> b = Item('bar')
>>> a < b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>

如果你使用元组 (priority, item) ,只要两个元素的优先级不同就能比较。但是
如果两个元素优先级一样的话,那么比较操作就会跟之前一样出错:

>>> a = (1, Item('foo'))
>>> b = (5, Item('bar'))
>>> a < b
True
>>> c = (1, Item('grok'))
>>> a < c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>

通过引入另外的 index 变量组成三元组 (priority, index, item) ,就能很好的避免上面的错误,因为不可能有两个元素有相同的 index 值。Python 在做元组比较时候,如果前面的比较以及可以确定结果了,后面的比较操作就不会发生了:

>>> a = (1, 0, Item('foo'))
>>> b = (5, 1, Item('bar'))
>>> c = (1, 2, Item('grok'))
>>> a < b
True
>>> a < c
True
>>>

如果你想在多个线程中使用同一个队列,那么你需要增加适当的锁和信号量机制。
可以查看 12.3 小节的例子演示是怎样做的。
heapq 模块的官方文档有更详细的例子程序以及对于堆理论及其实现的详细说明。 



6 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

3 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

7 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

8 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

9 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

10 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

11 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

12 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

13 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

14 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

3 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

15 解压可迭代对象赋值给多个变量

问题:

在迭代操作或者其他操作的时候,怎样只保留最后有限几个元素的历史记录?

解决方案:

保留有限历史记录正是 collections.deque 大显身手的时候。比如,下面的代码
在多行上面做简单的文本匹配,并返回匹配所在行的前 N 行:

from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for li in lines:
if pattern in li:
yield li, previous_lines
previous_lines.append(li)

讨论

我们在写查询元素的代码时,通常会使用包含 yield 表达式的生成器函数,也就是
我们上面示例代码中的那样。这样可以将搜索过程代码和使用搜索结果代码解耦。如
果你还不清楚什么是生成器,请参看 4.3 节。
使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并
且这个队列已满的时候,最老的元素会自动被移除掉。
代码示例:

>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)

尽管你也可以手动在一个列表上实现这一的操作 (比如增加、删除等等)。但是这里
的队列方案会更加优雅并且运行得更快些。
更一般的, deque 类可以被用在任何你只需要一个简单队列数据结构的场合。如
果你不设置最大队列大小,那么就会得到一个无限大小队列,你可以在队列的两端执
行添加和弹出元素的操作。
代码示例:

>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])

>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4

在队列两端插入或删除元素时间复杂度都是 O(1) ,而在列表的开头插入或删除元
素的时间复杂度为 O(N) 。

 

posted @ 2017-08-16 13:53  活的潇洒80  阅读(272)  评论(0编辑  收藏  举报