Python Cookbook读书笔记

好记性不如烂笔头,对之前阅读书籍进行梳理与总结,此文为《Python Cookbook》阅读笔记。

第一章 数据结构与算法

双端队列 collections.deque

(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)。

寻找最大或者最小的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和集合本身大小差不多,那么推荐先对集合排序,然后再做切片操作。

defaultdict

创建一个一键多值的字典是很容易的,但是如果尝试着自己对第一个值初始化操作,可能会变得很杂乱,如下

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函数/方法

大量的细节操作,去看书吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猴猴猪猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值