好记性不如烂笔头,对之前阅读书籍进行梳理与总结,此文为《Python Cookbook》阅读笔记。
(double ended queue)
from collections import deque
def search(lines, pattern, historcy=5):
previous_lines = deque(maxlen=history)
for line in lines:
if pattern in line:
yield line, previous_lines
previous_lines.append(line)
保存优先的历史记录可算是双端队列的完美应用场景,deque(maxlen = N)创建一个固定长度的队列,当有新的记录加入队列而队列已经满时,会自动移除掉最老的那条记录(准确地说是append,则移除掉最左端的记录,appendleft,则移除掉最右端的记录。)尽管可以用列表手动完成这些操作,但是队列这种解决方案会更加简洁优雅,且高效。从队列两端添加或者弹出元素的复杂度都是o(1),但是用列表来操作的话,从头部插入或者移除元素时,列表的复杂度为o(N)。
import heapq
num = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, num))
print(heapq.nsmallest(3, num))
上面的两个函数都可以接受一个参数key,从而允许它们工作在更加复杂的数据结构之上。
如果正在寻找最大或者最小的N个元素,且N相对于集合元素的总数目而言,N很小,那么下面这些函数可以提供更好的性能,这些函数首先会把底层数据转化为列表,且元素会以堆的顺序排列。
import heapq
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
heap = list(num)
heapq.heapify(heap)
# [-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 2]
堆最重要的特性就是heap[0]总是最小的那个元素,此外,接下来的元素可以通过headpq.headpop()方法轻松找到。(操作是o(logn))
当所找的元素数量相对较小时,函数nlargest()和nsmallest()才是最适用的,如果只是简单地寻找最小或者最大的元素(N=1)时,那么用min() or max()会更快,如果N和集合本身大小差不多,那么推荐先对集合排序,然后再做切片操作。
创建一个一键多值的字典是很容易的,但是如果尝试着自己对第一个值初始化操作,可能会变得很杂乱,如下
d = {}
for key, value in pairs:
if key not in d:
d[key] = []
d[key].append(value)
如果使用defaultdict会清晰很多
from collections import defaultdict
d = defaultdict(list)
for key, value in pairs:
d[key].append(value)
from collections import OrderDict
d = OrderDict()
当想构建一个映射结构一遍稍后对其做序列化或者编码称另外一种格式时,OrderDict就显得很有用,比如进行JSON编码想要精确控制各个字段的顺序,那么只需要在OrderDict中构建数据即可。
OrderDict在内部维护了一个双向链表,它会根据元素加入的顺序来排列键的位置,第一个新加入的元素会被放置在链表的末端,接下来对已存的键做重新赋值,不会改变键的顺序。
值得注意的是,OrderDict的大小是普通字典的2倍多,这是它额外创建的链表所导致。
如果讨论在字典上执行,常见的数据操作,将会发现它们只会处理键,而不是值,比如
price = {
'ACME' : 45.23,
'AAPL' : 612.78,
'IBM' : 205.55,
'HPQ' : 37.20,
'FS' : 10.75
}
min(price) # return ‘AAPL’
max(price) # return ‘IBM’
因此可以尝试利用zip方案,将原来的键-值对反转为值-键对,来进行求最值和排序操作,具体如下:
min_price = min(zip(price.values(),price.keys()))
# min_price is (10.75, 'FB')
max_price = max(zip(price.values(),price.keys()))
# max_price is (612.78, 'AAPL')
price_sorted = sorted(zip(price.values(),price.keys()))
当进行这些计算时,请注意zip()创建的事一个迭代器,它的内容只能被消费一次,所以下面的代码是错误的:
price_and_name = zip(price.values(),price.keys())
min_price = min(price_and_name) # OK
max_price = max(price_and_name) # ValueError: max() arg is an empty sequence
字典就是一系列键和值之间的映射集合。字典的keys()方法会返回key-view对象,其中暴露了所有的键,其支持常见的集合操作,比如求交,并,差等等,因此如果需要把字典的键做常见的集合操作,那么可以直接使用key-view对象,而不必将它们先转化为集合,对于items()同理(其返回items-view对象),但是对于values()而言,从值的角度来看,并不能保证所有的值都是唯一的,因此需要先转化为集合再实现,
# find keys in common
a.keys() & b.keys()
# find keys in a that are not in b
a.keys() - b.keys()
# find (key, value) pairs in common
a.items() & b.items()
collections模块中的Counter类,提供了一个most_common()方法
可以给Counter类提供任何可以哈希的对象序列作为输入,在底层实现中,Counter是一个字典,在元素和它们出现的次数间做了映射。关于Counter对象有一个不为人知的特性,那就是它们可以轻松地同各类数学运算操作结合起来使用。
from oprator import itemgetter
rows_by_uid = sorted(rows, key = itemgetter('uid'))
其中rows是一个列表,其中每一个元素都是一个字段,包含fname, uid等字段
有时候也用lambda表达式来取代itemgetter()的功能,例如:
rows_by_id = sorted(rows, key = lambda r : r['uid'])
但是用itemgetter()通常会运行得相对更快一些
我们想在同一个类的实例之间做排序,但是它们并不原生支持比较操作。
内置的sorted函数,可以接受一个用来传递可调用(callable)对象的参数key,而该可调用对象会返回待排序对象中的某个值,sorted则利用这些值进行比较对象。
比如:
sorted(users, key = attrgetter('user_id')
#或者用lambda表达式
sorted(users, key = lambda u : u['user_id'])
attrgetter()相对lambda表达式运行速度会快一些,且允许同时提取多个字段
by_name = sorted(user, attrgetter('laste_name', 'first_name'))
值得一提的是,本节所用到的技术,同样适用于min(), max()这样的函数。
字典推导式
# make a dictionary of all prices over 200
p1 = {key:value for key, value in prices.items() if value > 200}
# make a dictionary of tech stocks
tech_names = {'AAPL', 'IBM', 'HPQ', 'MSFT'}
p2 = {key:value for key, value in prices.items() if key in tech_names}
from collections import ChainMap
a = {'x': 1, 'z':3}
b = {'y': 2, 'z':4}
c = ChainMap(a, b)
ChainMap可以接受多个映射,并且在逻辑上使得它们表现为一个单独的映射结构,但是这些映射在字面上不会和并在一起,如果有重复的键,那么这里会采用第一个映射中的值。如果原始的字典,值发生变化,ChainMap该对象也发生了变化,即逻辑上是一个单独的映射结构,但是物理上还是原来的映射本身。
import re
line = 'asdf fjdk;afed,fjek,asdf, foo'
re.split(r'[;,\s]\s*', line)
startswith()和endswith()方法,如果需要同时针对多个选项做检查,只需给startswith()和endswith()提供包含可能选项的元组即可。
简单的文本模式,可以使用str.replace()即可
对于复杂的模式,可以引入re模块中的sub函数/方法
大量的细节操作,去看书吧