note-PythonCookbook-第一章 数据结构和算法

第一章 数据结构和算法

1.2 可迭代对象的解压

用*号解压未知个数的参数;
解压出来的结果总是列表类型。

1.3 保留最新的几个元素

from collections import deque
q = deque(maxlen=n)
q.append()

不赋值会创建无限大小的队列, 可进行两端的操作。
队列两端的添加删除操作会比列表快.(O(1),O(n))。

q = deque()
q.append()
q.appendleft()
q.pop()
q.popleft()

1.4 找最大或最小的n个元素

import heapq
# 找到数据集中最大最小的n个元素
heapq.nlargest(n, iterable, key=None)
heapq.nsmallest(n, iterable, key=None)
# 把堆中最小的元素弹出
heapq.heappop()

n=1时:max(),min()效率最高;
n较小时:用heapq;
n接近于数据集的大小:用sorted(iterable, key)[:n]

1.5 实现一个优先级队列

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]

优先级用负是为了让优先级最大的处于堆顶部;
index 支持同等优先级的元素的比较。

1.6 字典中的键映射多个值

把多个值放到容器中。根据需要,列表或集合

from collections import defaultdict
d = defaultdict(list)
d['a'].append(1)
d['b'].append(3)
d['a'].append(4)

d = defaultdict(set)
d['a'].add(1)

defaultdict 会自动为访问的键创建映射,不管键是否存在;
setdefault 可用于普通字典,可对不存在的键人为赋初值。

d = {}
d.setdefault('a', []).append(1)
d.setdefault('b', []).append(3)
d.setdefault('a', []).append(4)
d.setdefault('a')   # 返回键对应的值
d.setdefault('d')   # 默认赋初值 None

1.7 字典排序

collections 中的 OrderedDict 会按键插入顺序维护一个链表,重复赋值不会改变键的顺序
大小是普通字典的两倍

1.8 字典的运算

用 zip() 翻转键和值后再进行运算。

zip(d.valuues(), d.keys())

zip() 函数生成的是只能使用一次的迭代器。

1.9 查找两字典的相同点

字典的 keys(),items() 方法得到的对象,支持集合操作

a.keys() - b.keys()
a.keys() & b.keys()
a.keys() | b.keys()

a.items() & b.items()

1.10 删除序列相同元素并保持顺序

序列元素可哈希(数值、字符串、元组):

a = [1,5,3,1,9,3,4]
def dedupe1(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

list(deque1(a))

序列元素不可哈希(字典、列表、集合):

b = [{'x':1, 'y':2},{'x':1, 'y':3},{'x':2, 'y':2}]
def dedupe2(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)

list(dedupe2(b, key=lambda i: i['x']))
list(dedupe2(b, key=lambda i: (i['x'], i['y']))

1.11 命名切片

a = '123456789abcdefg'
print(a[5:10])
# 能增加可读性
data_slice = slice(5, 10)
print(a[data_slice])
# 获取 slice 的信息
data_slice.start    # 5
data_slice.stop     # 10
data_slice.step     # None
# indices 方法能避免下标越界
data_slice = slice(5, 100)
data_slice.indices(len(a))      # (5, 16, 1)
for i in range(*data_slice.indices(len(a))):
    print(a[i])

1.12 序列中出现次数最多的元素

from collections import Counter
a = 'abab'
b = 'abc'
aa = Counter(a)     # Counter({'a': 2, 'b': 2})
bb = Counter(b)     # Counter({'a': 1, 'b': 1, 'c': 1})
aa-bb               # Counter({'a': 1, 'b': 1})
aa + bb             # Counter({'a': 3, 'b': 3, 'c': 1})
aa.update(b)        # 等价于 aa = aa + Counter(b)

1.13 通过某个关键字排序一个字典列表

from operator import itemgetter
a = [{'a':2, 'b':4, 'c': 'ax'},
	 {'a':1, 'b':2, 'c': 'mj'},
	 {'a':5, 'b':6, 'c': 'e'}]

res = sorted(a, key=itemgetter('a'))
res = sorted(a, key=itemgetter('a', 'b'))

itemgetter('a')(a[0])       # 2
itemgetter('a', 'b')(a[0])  # (2, 4)

等价于 lambda x: (x[‘a’], x[‘b’]),但比 lambda 更快。

1.14 排序不支持原生比较的对象

from operator import attrgetter

用法和优点类似 itemgetter 只是作用于对象

1.15 通过某个字段将记录分组

data = [{'a':'x01', 'y':'2019'},{'a':'x02', 'y':'2020'},{'a':'x03', 'y':'2019'}]
from itertools import groupby
from operator import itemgetter

data.sort(key=itemgetter('y'))
for y, aa in groupby(data, key=itemgetter('y')):
    print(y)
    for a in aa:
        print(' ', a)

"""result
2019
  {'a': 'x01', 'y': '2019'}
  {'a': 'x03', 'y': '2019'}
2020
  {'a': 'x02', 'y': '2020'}
"""

groupby 会扫描整个序列找出连续的相同值,返回该值和一个迭代器对象;
只检查连续的元素,所以必须事先排好序;
把结果存在 defaultdict 创建的多值字典中便于访问。

1.16 过滤序列元素

一般用列表推导式;
数据量大又不想太耗内存用生成器表达式;
如果过滤规则复杂,就用 filter() 函数。

data = [n for n in range(1000) if n%2==0]

def rules(num):
	if num % 3 == 0 and num % 7 == 0:
		return True
filter(rules, data)

不符合条件的值替换为新值:

data = [n if n%2==0 else n-1 for n in range(1000)]

想用一个序列的值来过滤另一个序列:

from itertools import compress
names = ['Wilson', 'Sandy', 'Carrol', 'Dante', 'Aaron']
heights = [181, 167, 175, 178, 173]
bool_list = [i>175 for i in heights]
compress(names, bool_list)		# 返回迭代器

1.17 从字典中提取子集

# 字典推导式(最快)
dict2 = {key:value for key,value in dict1.items() if key.isdigit()}
# 元组转字典
dict2 = dict((key, value) for key,value in dict1.items() if key.isdigit())
# 先过滤键再取值
dict2 = {key:dict1[key] for key in dict1.keys() if key.isdigit()}

1.18 映射名称到序列元素

想通过名称来访问列表或元组中的元素。collections.namedtuple 能满足这个需求。这个方法返回标准元组类型的子类。
使用命名元组时需要传递一个类名和一个字段名序列,然后返回一个类;
实例化的时候传入对应长度的值序列,访问这些值的方式和访问实例的属性一样。

from collections import namedtuple
Cal = namedtuple('Cal', ['name', 'shares', 'price'])
total = 0
for rec in records:
	s = Cal(*rec)
	total += s.shares * s.price

命名元组的使用能避免可读性低的下标操作。
命名元组能作为字典的替代,它比字典省内存且高效,支持索引和解压操作,但是命名元组是不可改的。想要改变实例属性,能用 _replace 方法返回一个新的命名元组。

c = Cal('banana', 36, 3.0)
c = c._replace(shares=38)
# Cal(name='banana', shares=36, price=3.0)

如果需要更新很多实例,不适合用命名元组。

1.19 转换并同时计算数据

在序列上执行聚集函数前需要先加工序列时,生成器表达式简洁又高效。
函数调用时直接输入表达式,不需要多加一个括号。

sum(i for i in range(1001) if i%2!=0)

1.20 合并多个字典或映射

从多个字典中,查找值或确定键是否存在。

from collections import ChainMap
a = {'a':1, 'b':3, 'x':5}
b = {'a':3, 'c':6, 'y':8}
c = ChainMap(a, b)
# 先从a中查找,找不到再去b中找。所有字典都找不到会报错。
c['a']	# 1
c['c']  # 6

对 ChainMap 的更新或删除操作只会影响第一个字典。
原字典更新时,ChainMap 也会有相应的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值