Loading

《Python Cookbook v3.0.0》Chapter1 数据结构和算法

感谢:
https://github.com/yidao620c/python3-cookbook
如有侵权,请联系我整改。

本文章节会严格按照原书(以便和原书对照,章节标题可能会略有修改),内容会有增删。

1.1 解引、解引赋值

1.1.1 解引

通过*可以解引(书中翻译为解压,还是解引更舒服些),
示例,

>>> (4,5)
(4, 5)
>>> print(*(4,5))
4 5
>>> str='akdfjasl'
>>> str
'akdfjasl'
>>> print(*str)
a k d f j a s l
>>> d={'a':1,'b':2}
>>> d
{'a': 1, 'b': 2}
>>> print(*d)
a b

1.1.2 解引赋值

可以用占位符,丢弃不要的数据。
示例,

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

通过*可以赋值多个变量,
实例,

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

1.3 只保留N个元素

from collections import deque
示例,

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

1.4 最大/最小的N个元素

import heapq
示例,

>>> 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'])
>>> cheap
[{'name': 'YHOO', 'shares': 45, 'price': 16.35}, {'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'HPQ', 'shares': 35, 'price': 31.75}]

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

示例,

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

1.5 优先级队列

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

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,作用是当item本身不支持比较的时候,在priority相同时,可以通过index
比较,即,按照插入顺序排序

1.6 multidict(多值字典)

from collections import defaultdict
比如,

d = {
  'a' : [1, 2, 3],
  'b' : [4, 5]
}
e = {
  'a' : {1, 2, 3},
  'b' : {4, 5}
}

值可用list、set等存储

d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(4)
d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)

defaultdict维护非常便利

1.7 OrderedDict(顺序字典)

from collections import OrderedDict

如果想控制字典顺序,可以用OrderedDict
[TODO]

1.8 字典运算

可以用zip打包成元组,

prices = {
  'ACME': 45.23,
  'AAPL': 612.78,
  'IBM': 205.55,
  'HPQ': 37.20,
  'FB': 10.75
}
min_price = min(zip(prices.values(), prices.keys()))
# min_price is (10.75, 'FB')

也可以用key

min(prices, key=lambda k: prices[k])

1.9 字典的异同

可以用keys()items()方法

a = {
  'x' : 1,
  'y' : 2,
  'z' : 3
}
b = {
  'w' : 10,
  'x' : 11,
  'y' : 2
}
# Find keys in common
a.keys() & b.keys() # { 'x', 'y' }
# Find keys in a that are not in b
a.keys() - b.keys() # { 'z' }
# Find (key,value) pairs in common
a.items() & b.items() # { ('y', 2) }
# Make a new dictionary with certain keys removed
c = {key:a[key] for key in a.keys() - {'z', 'w'}}
# c is {'x': 1, 'y': 2}

注意,字典的values()并不支持上述操作,原因是,值并不一定是相同的形式,
确切的讲,值中的元素个数可能是不同的;而键虽然形式也可能不同,但个数永远是1

1.10 删除相同元素,但保持原顺序

set结合yield

def dedupe(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)

key方法的原因是,items并不一定是hashable的,比如dict

示例,

>>> a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
>>> list(dedupe(a, key=lambda d: (d['x'],d['y'])))
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
>>> list(dedupe(a, key=lambda d: d['x']))
[{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]

注意,使用set()构造,或者使用set.add()都会改变顺序,后者还会排序

1.11 切片

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[a] = [10,11]
>>> del items[a]

1.12 次数最多的元素

from collections import Counter
示例,

word_counts = Counter(words)
top_three = word_counts.most_common(3)
# Outputs [('eyes', 8), ('the', 5), ('look', 4)]

其中words可以是listtupple,当然set也可以,不过没有意义
Counter()返回的结果类似字典,形如,

Counter({'eyes':8,'the':5})

实际上,它的类型是<class 'collections.Counter'>

注意,虽然它看起来像字典,但某些操作和字典不同,比如,
update,在dict中,update意味着新值覆盖旧值,而在Counter中,
update意味着新增计数,在原有基础上增加

>>> d
{'a': 1, 'b': 2}
>>> d2
{'a': 1, 'b': 3}
>>> d.update(d2)
>>> d
{'a': 1, 'b': 3}
>>> w=dict(d)
>>> w
{'a': 1, 'b': 3}
>>> cw=Counter(w)
>>> cw
Counter({'b': 3, 'a': 1})
>>> w2=dict(w)
>>> cw.update(w2)
>>> cw
Counter({'b': 6, 'a': 2})

同时,Counterupdate,入参也可以是另一个Counter
除了updateCounter还支持+-运算

1.13 通过关键字给字典列表排序

from operator import itemgetter
示例,

rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))

itemgetter的效率比lambda高,结果是等价的

1.14 通过关键字给结构体列表排序

这里和1.13的区别是,上面是dict,这里是class
from operator import attrgetter
示例,

by_name = sorted(users, key=attrgetter('last_name', 'first_name'))

不限于sortedminmax都可以用,同itemgetter一样,效率比lamdba高,
至于,为何class不能用itemgetter,是因为key必须是一个callable对象,即,
()调用的,itemgetter一个class的属性,无法创建这样一个对象

1.15 通过某个字段分组

比如一堆记录,按日期进行分组。
from itertools import groupby

注意,groupby会按照顺序处理,所以在使用前,需要先进行排序

示例,

rows = [
  {'address': '5412 N CLARK', 'date': '07/01/2012'},
  {'address': '5148 N CLARK', 'date': '07/04/2012'},
  {'address': '5800 E 58TH', 'date': '07/02/2012'},
  {'address': '2122 N CLARK', 'date': '07/03/2012'},
  {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
  {'address': '1060 W ADDISON', 'date': '07/02/2012'},
  {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
  {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]
rows.sort(key=itemgetter('date'))
for date, items in groupby(rows, key=itemgetter('date')):
  print(date)
  for i in items:
    print(' ', i)

07/01/2012
  {'date': '07/01/2012', 'address': '5412 N CLARK'}
  {'date': '07/01/2012', 'address': '4801 N BROADWAY'}
07/02/2012
  {'date': '07/02/2012', 'address': '5800 E 58TH'}
  {'date': '07/02/2012', 'address': '5645 N RAVENSWOOD'}
  {'date': '07/02/2012', 'address': '1060 W ADDISON'}

groupby返回的是一个迭代器,内容类似一个tupple

1.16 列表推导

示例,

>>> mylist = [1, 4, -5, 10, -7, 2, 3, -1]
>>> import math
>>> [math.sqrt(n) for n in mylist if n > 0]         //过滤,并计算
>>> clip_neg = [n if n > 0 else 0 for n in mylist]  //值替代
>>> more5 = [n > 5 for n in counts]                 //返回true、false list

其中过滤,也可以用filter实现
true、false list可以结合from itertools import compresscompress来压缩表项

1.17 字典推导

prices = {
  'ACME': 45.23,
  'HPQ': 37.20,
  'FB': 10.75
}
p1 = {key: value for key, value in prices.items() if value > 200}
tech_names = {'AAPL', 'IBM', 'HPQ', 'MSFT'}
p2 = {key: value for key, value in prices.items() if key in tech_names}

推导,比dict()构造效率高

1.18 命名元组

from collections import namedtuple

不再通过下标访问,而是通过类似字典的方式访问
虽然是tupple,但提供了_replace方法来修改元素内容

示例,

Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])
stock_prototype = Stock('', 0, 0.0, None, None)
# Function to convert a dictionary to a Stock
def dict_to_stock(s):
  return stock_prototype._replace(**s)

>>> a = {'name': 'ACME', 'shares': 100, 'price': 123.45}
>>> dict_to_stock(a)
Stock(name='ACME', shares=100, price=123.45, date=None, time=None)

这里的字典解引很有意思,它等效于

stock_prototype._replace(name='acmd',shares=999)

1.19 聚集函数的推导

原文标题是,转换并同时计算数据。但看起来,形式类似推导。
聚集函数,summinmax等。
示例,

s = sum(x * x for x in nums)
min_shares = min(s['shares'] for s in portfolio)

minmax中,效果等效使用key

1.20 合并多个字典或映射

前文有讲过普通字典dict以及Counterupdate,
这里是另一种形式,
from collections import ChainMap
从名字上,它就是chain,链,将dict链起来,

当多个dict有键值重复时,返回首个值

posted @ 2021-08-15 16:03  wwcg2235  阅读(47)  评论(0)    收藏  举报