1.1 将序列分解为单独的变量
p = [4,5,(1010,2,1)]
x, z, (y, m, d)=p
print(x, z, y, m, d)
>>4 5 1010 2 1
s = 'hello'
a, b, c, d, e = s
print(a,b,c,d,e)
>>h e l l o
复制代码
- 不仅是元组或列表,只要是可迭代对象就可以执行分解操作,包括元组、列表、字符串、文件、迭代器或生成器。
- 分解过程中,若需要丢弃某些值,可以选择一个用不到的变量名来重复接收,例如
a, _, c, _, d = 'hello'
复制代码
1.2 从任意长度的可迭代对象中分解元素
records = ['dave','dave@qq.com','138934594567','34565647567','234234-45645-567']
name, email, *phnoe_numbers = records
print(phone_numbers)
>>['138934594567','34565647567','234234-45645-567']
复制代码
- 无论是否有值,变量phone_numbers始终是一个列表 。
- 如下写法,也是可以的
*others, last_phone_numbers = record
name, *others, last_phone_numbers = record
复制代码
- 若想分解出某些值,然后丢弃,同意可以选择一个用不到的变量名来重复接收,比如 _ 或者 ign(ignored),例如
records=['ace','1',2,3,(12,56,21)]
name,*_(*_,age)=records
复制代码
- 这种分解操作可以假设是一种精巧的递归算法,但递归不是python的强项, 且有递归次数限制,默认是1000(可以修改)。
扩展使用
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)
复制代码
1.3 保存最后N个元素
-
问题:希望在迭代或其他形式处理中,对最后几个记录做一个有限的历史记录统计。
-
解决:collections.deque队列的完美应用场景。列表也可以实现,但队列更优雅,速度更快。
#使用示例:
#指定队列大小为3
q = deque(maxlen = 3)
q.append(1)
q.append(2)
q.append(3)
print(q)
>>deque([1,2,3],maxlen=3)
q.append(4)
print(q)
>>deque([2,3,4],maxlen=3)
#不指定队列大小==无界限队列,可在队列两端执行添加/弹出操作
q2=deque()
q2.append(1)
q2.append(2)
print(q2)
>>deque([1,2])
q2.appendleft(4)
print(q2)
>>deque([4,1,2])
print(q2.pop())
>>2
print(q2)
>>deque([4,1])
print(q2.popleft())
>>4
复制代码
从队列两端添加或弹出元素的复杂度都是O(1)。这和列表不同,当从列表的头部插入或移除元素时,复杂度是O(N)
- 使用案例:对一系列文本行做简单的文本匹配操作,当发现有匹配时就输出当前的匹配行以及最后检查过的N行文本(这里的N就是下面的history)
from collections import deque
#这是个生成器
def search(lines,pattern,history=5):
#创建一个最多只能存储5个元素的队列(固定长度队列),当有新记录加入而队列已满时会自动移除最老的那条记录
previous_lines=deque(maxlen=history)
for line in lines:
if pattern in line:
#遇到yield就会返回,同时也是下一次循环进入的位置
yield line,previous_lines
previous_lines.append(line)
if __name__=='__main__':
with open('somefile.txt') as f :
#传入可迭代对象f,匹配字符串python,保留匹配行之前的5个元素
for line,previous_lines in search(f,'python',5):
#循环打印匹配行之前的5个元素
for p in previous_lines:
print(p,end='')
#打印匹配行
print(line,end='')
print('-'*20)
复制代码
1.4 找到最大或最小的N个元素
- 问题:在某个集合或列表中找出最大或最小的N个元素
- 解决:包含一些堆操作函数的模块--heapq模块,其中有2个函数--nlargest()和nsmallest()
模块heapq中一些重要的函数
函 数 | 描 述 |
---|---|
heappush(heap, x) | 将x压入堆中(若heap是空列表,则使得heap列表具备堆特征,免除heapify()方法),复杂度O(logN),效率高 |
heappop(heap) | 从堆中弹出最小的元素,复杂度O(logN),效率高 |
heapify(heap) | 让列表具备堆特征,也是上述两个方法的先决条件 |
heapreplace(heap, x) | 弹出最小的元素,并将x压入堆中 |
nlargest(n, iter) | 返回iter中n个最大的元素 |
nsmallest(n, iter) | 返回iter中n个最小的元素 |
#使用示例
import heapq
nums=[1,8,2,23,7,-4,18,23,42,37,2]
print(heapq.nlargest(3,nums))
>>[42,37,23]
print(heapq.nsmallest(3,nums))
>>[-4,1,2]
复制代码
- 扩展:这两个函数都可以接收一个参数key,从而工作在更加复杂的数据结构之上。
p=[
{'name':'AAPL','price':20},
{'name':'FB','price':3},
{'name':'QQ','price':56},
{'name':'WX','price':43},
{'name':'WX','price':43}
]
#使用匿名函数,重新指定比较对象,这里的d是列表p里的某个元素
cheap=heapq.nsmallest(2,p,key=lambda d: d['price'])
expensive=heapq.nlargest(2,p,key=lambda d: d['price'])
复制代码
- 讨论 :如果正在寻找最大或最小的N个元素,同集合中的元素的总数相比,N很小,那么使用下面的函数性能会更好。
- 工作原理:这些函数首先会在底层将数据转化成列表,且元素会以堆的顺序排列。
- 特性:堆最重要的特性就是h[0]总是最小的那个元素。接下来的元素可依次通过heapq.heappop()方法找到,该方法会将第一个元素(最小的)弹出,然后以第二小的元素取而代之(复杂度O(logN),N代表堆大小)
import heapq
nums=[1,8,2,23,7,-4,18,23,42,37,2]
h=list(nums)
heapq.heapify(h)
print(h)
>>[-4,2,1,23,7,2,18,23,42,37,8]
#要找到第3小的元素可以这样
heapq.heappop(h)
>>-4
heapq.heappop(h)
>>1
heapq.heappop(h)
>>2
复制代码
- 总结:
- 当所要找的元素数量相对较小时,用nlargest()和nsmallest()才是最适合的。注意,这两个方法的实际实现会根据使用它们的方式而有所不同,会做一些优化措施(比如,当N的大小同输入大小很接近时,就会采用排序的方法)
- 如果只想找出最小或最大的某个元素,用min()或max()。
- 如果要找的元素N和集合本身的大小差不多,通常更快的方法是先对集合排序,再做切片(sorted(items)[:N]或者sorted(items)[-N:])
1.5 实现优先级队列
- 问题:实现一个队列,它能够以给定的优先级来对元素排序,且每次pop操作时都会返回优先级最高的那个元素。
- 解决:利用 堆heapq模块 实现简单的优先级队列
import heapq
class PriorityQueue:
def __init__(self):
self._queue=[]
self._index=0
def push(self,item,priority):
#堆的特性是self._queue[0]总是最小的那个元素,加个-号就是为了把最大值变最小值
#变量self._index在具有相同优先级的元素间,作为比较操作的第二选择
heapq.heappush(self._queue,(-priority,self._index,item))
self._index+=1
def pop(self):
# heapq.heppop(self._queue) == 最小元素(-priority,self._index,item),
# heapq.heppop(self._queue)[-1] == item
return heapq.heppop(self._queue)[-1]
class Item:
def __init__(self,name):
self.name=name
#打印对象的时候调用该函数
def __repr__(self):
return 'Item({!r})'.format(self.name)
#str()函数: 主要面向用户,其目的是可读性,返回形式为用户友好性和可读性都较强的字符串类型;
#repr()函数:面向的是python解释器,或者说开发人员,其目的是准确性,其返回值表示python解释器内部的含义,常作为编程人员debug用途。
q=priority()
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')
复制代码
请注意,如果有相同优先级的两个元素(foo和bar),返回的顺序和它们插入到队列时的顺序相同。