第一章:数据结构和算法
1.1解压序列赋值给多个变量
如果变量个数和序列元素个数不匹配,会产生一个异常。
代码示例:
p = (4,5)
x, y, z = p
#会显示ValueError:need more than 2 values to uppack
#此时删掉z则error会消失
有条件的可以试一下以上代码
当元组有三个元素,但是你只需要中间一个元素,可以把第一个和第三个元素用其他变量去占位,到时候丢掉即可。
1.2解压可迭代对象赋值给多个变量
可用Python的*来解决这个问题。
def drop_first_last(grades):
dirst, *middle, last = grades
return avg(middle)
当运行时你会发现,middle的输出结果类型为列表类型。
grades数据类型可为:列表、元组、字符串
比较适合可变长元组序列。
也可用在字符串:
lines = 'E:/python_work/pachong/crawler/ab.py'
*trailing, current = lines.split('/')
print(trailing)
'''
运行结果:
F:\python\python.exe E:/python_work/pachong/crawler/ab.py
['E:', 'python_work', 'pachong', 'crawler']
Process finished with exit code 0
'''
如果你够聪明也可用这种分割语法实现递归算法。
1.3保留最后N个元素
可以使用collections.deque
下面的代码在多行上面做简单的文本匹配,并返回匹配所在行的最后N行:
查询元素的代码时,通常会yield表达式的生成器函数。
使用**deque(maxlen=N)**构造函数会新建一个固定大小的队列。当新元素加入时会移除最先加入的元素。(队列的性质)
from collections import deque
q = deque(maxlen=3)
q.append(1)
print(q)
q.append(2)
q.append(3)
print(q)
q.append(4)
print(q)
#运行结果:
F:\python\python.exe E:/python_work/pachong/crawler/collections_deque.py
deque([1], maxlen=3)
deque([1, 2, 3], maxlen=3)
deque([2, 3, 4], maxlen=3)
Process finished with exit code 0
也可不设置队列大小,那么这个对列就无限大,可在队列两端执行添加和弹出元素的操作。
q = deque()
q.append(1)
q.append(2)
q.append(3)
print(q)
q.appendleft(4)
print(q)
print(q.pop())
print(q)
print(q.popleft())
#运行结果
F:\python\python.exe E:/python_work/pachong/crawler/collections_deque.py
deque([1, 2, 3])
deque([4, 1, 2, 3])
3
deque([4, 1, 2])
4
Process finished with exit code 0
在队列两端插入或删除元素的时间堵咋堵啊都是O(1),而在队列的开头插入或删除的时间复杂度为O(N).
1.4查找最大或最小的N个元素
使用heapq模块,heapq有两个函数:
nlargest():取最大值
nsmallest():取最小值
下面的代码用price的值进行比较
import heapq
nums = [1, 4, 5, 8, 9, 44, 55, 36, 85, 41, 1]
print(heapq.nlargest(3, nums)) #输出最大的三个值
print(heapq.nsmallest(3, nums)) #输出最小的三个值
protfolio = [
{'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, protfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, protfolio, key=lambda s: s['price'])
print(cheap)
print(expensive)
heap = list(nums)
heapq.heapify(heap)
print(heap)
print(sorted(nums)[:11])
print(set(nums))
#运行结果:
F:\python\python.exe E:/python_work/pachong/crawler/module_heapq.py
[85, 55, 44]
[1, 1, 4]
[{'name': 'YHOO', 'shares': 45, 'price': 16.35}, {'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'HPQ', 'shares': 35, 'price': 31.75}]
[{'name': 'AAPL', 'shares': 50, 'price': 543.22}, {'name': 'ACME', 'shares': 75, 'price': 115.65}, {'name': 'IBM', 'shares': 100, 'price': 91.1}]
[1, 1, 5, 8, 4, 44, 55, 36, 85, 41, 9]
[1, 1, 4, 5, 8, 9, 36, 41, 44, 55, 85]
{1, 4, 5, 36, 8, 9, 41, 44, 85, 55}
Process finished with exit code 0
下面代码为对列表进行一次堆排序,然后可以得到最小值heap[0],弹出的元素始终为该列表最小值最小。时间复杂度为O(logN).
nums = [1, 8, 3, 4, 5, 9, 6, 77, -54, 22]
import heapq
heap = list(nums)
heapq.heapify(heap)
print(heap)
#输出最小的三个元素
print(heapq.heappop(heap)) #输出第一个元素
print(heapq.heappop(heap))
print(heapq.heappop(heap))
小结:
当查找元素个数相对比较小,可用函数nlargest()和nsmallest().
当查找唯一最大或最小值,可用函数max()和min().
当N的大小接近集合一般先排序再切片可用sorted(items)[:N](前N个最小值)或sorted(items)[-N:](后N个最大值)。
1.5实现一个优先队列
使用heapq实现简单的优先级队列:
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]
class Item:
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Item({!r})'.format(self.name)
q = PriorityQueue()
q.push(Item("foo"), 1)
q.push(Item("bar"), 5)
q.push(Item("spam"), 4)
q.push(Item("grok"), 1)
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())
运行结果:
F:\python\python.exe E:/python_work/pachong/crawler/priority_queue.py
Item('bar')
Item('spam')
Item('foo')
Item('grok')
Process finished with exit code 0
第一个pop()返回优先级最高的元素。相同优先级按照插入顺序返回。
讨论
函数heapq.heappus()he heapq.heappop()分别在独立额上插入和删除第一个元素,并且队列_queue第一个元素具有最高优先级。heappop()函数总是返回“最小(最小位置)”的元素。push和pop操作复杂度都是O(logN),N是堆的大小。
1.6字典中的键映射多个值
该字典使用列表还是集合取决于你的实际需要。如果你想保持插入顺序,那么就用列表;如果想你想消除重复元素那么使用集合。
那么可以使用collections模块中的defaultdict来构造这样的字典。defaultdict会总动初始化每个key刚开始对应的值,所以你需要关注添加元素操作。
from collections import defaultdict
d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(4)
print(d)
d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)
print(d)
#运行结果:
F:\python\python.exe E:/python_work/pachong/crawler/clooections_defaultdict.py
defaultdict(<class 'list'>, {'a': [1, 2], 'b': [4]})
defaultdict(<class 'set'>, {'a': {1, 2}, 'b': {4}})
Process finished with exit code 0
defaultdict会自动创建要访问的键创建映射实体。如果不需要,则可用setdefault()方法来替代。
d = {}
d.setdefault('a', []).append(1)
d.setdefault('a', []).append(2)
d.setdefault('b', []).append(4)
print(d)
运行结果:
{'a': [1, 2], 'b': [4]}
那么创建一个多映射字典就很简单了
d = defaultdict(list)
for key, value in pairs:
d[key].append(value)
1.7字典排序
为了控制一个字典中的顺序,可以使用collections模块中的OrdereDict类,在迭代在操作的时候他会保持元素插入时的顺序。
from collections import OrderedDict
d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4
for key in d:
print(key, d[key])
讨论
OrdereDict内部维护着一个根据键插入顺序排序的双向链表,每次当一个新的元素插入时,把它放在链尾。对于一个已经存在的键的重复赋值不会改变键的顺序。
OrdereDict的大小是普通字典的两倍,因为它内部维护着一个链表。
1.8 字典的运算
使用zip函数将键和值反转,再查找最小和最大股票价格和股票值的代码。
min_price = min(zip(prices.values(),prices.keys()))
# min_price is (10.75, 'FB')
max_price = max(zip(prices.values(),prices.keys()))
# max_price is (612.78, 'AAPL')
#使用sorted()函数排列字典数据
prices_sorted = sorted(zip(prices.values(), price.keys()))
# prices_sorted is [(10.75, 'FB'), (37.2, 'HPQ'),
# (45.23, 'ACME'), (205.55, 'IBM'),
# (612.78, 'AAPL')]
zip()函数创建的是一个只能访问一次的迭代器。
zip()函数,zip(值values,键keys)
讨论
但是如果还想要得到最小值
min_value = prices[min(prices, key = lambda k: prices[k])]
1.9查找量子点的相同点
a = {
'x': 1,
'y': 2,
'z': 3
}
b = {
'w': 10,
'x': 11,
'y': 2
}
print(a.keys() & b.keys())
print(a.keys() - b.keys())
print(a.items() & b.items())
c = {key: a[key] for key in a.keys() - {'z', 'w'}}
print(c)
#运行结果:
F:\python\python.exe E:/python_work/pachong/crawler/one/dict_equals.py
{'y', 'x'}
{'z'}
{('y', 2)}
{'y': 2, 'x': 1}
Process finished with exit code 0
字典就是键集合和值集合的映射,字典的keys()就是键视图对象,字典的items()是字典中中每项(包括键和值)的元素视图对象,可以对其进行并、交和差的运算。values()就是值视图对象,,他不支持这里介绍的集合操作,是因为值视图不能保证所有的值是不同的,可以先使用set再操作。
1.10 删除序列相同元素并保持顺序
可以使用集合或者生成器来解决。
def deque(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
a = [1, 5, 5, 8, 9, 6, 2, 1, 7, 1]
print(list(deque(a)))
#运行结果
F:\python\python.exe E:/python_work/pachong/crawler/one/deque.py
[1, 5, 8, 9, 6, 2, 7]
Process finished with exit code 0
这里只能是元素为hashable(不可变)的时候才管用。但如果是不可哈希的序列呢?
def deque(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)
a = [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2},{'x':2, 'y':4}]
print(list(deque(a, key=lambda d: (d['x'], d['y']))))
print(list(deque(a, key=lambda d: d['x'])))
#运行结果
F:\python\python.exe E:/python_work/pachong/crawler/one/deque.py
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
[{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
Process finished with exit code 0
key参数指定函数,将序列元素转换为hashable类型。
set函数不能维护元素的是顺序。