python cookbook3读书笔记第一章数据结构和算法

python heapq模块查询一组序列中最大和最小的数据


import heapq

nums= [1, 8, 2, 23, 7,-4, 18, 23, 42, 37,]

#获取序列中3个最大值
# print(heapq.nlargest(3,nums))
#获取序列中3个最小值
# print(heapq.nsmallest(3,nums))
#把数据压入堆中在堆中最小的那个数值永远排在最前面时间想取出最小的3个数值只需执行3次heapq.heapify(nums)时间复杂度为O(n)
heapq.heapify(nums)
print(nums)
heapq.heappop(nums)
print(nums)
heapq.heappop(nums)
print(nums)
heapq.heappop(nums)
print(nums)
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}
]

print(heapq.nlargest(3,portfolio,key=lambda x:x['price']))
print(heapq.nsmallest(3,portfolio,key=lambda x:x['price']))

上面代码在对每个元素进行对比的时候,会以 price 的值进行比较

当要查找的元素个数相对比较小的时候,函数 nlargest() 和 nsmallest() 是很合适的。
如果你仅仅想查找唯一的最小或最大(N=1)的元素的话,那么使用min()和max()函数会更快些。
类似的,如果N的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点 ( sorted(items)[:N] 或者是 sorted(items)[-N:] )。
需要在正确场合使用函数nlargest() 和 nsmallest()才能发挥它们的优势 (如果N快接近集合大小了,那么使用排序操作会更好些.


*号表达式

items = [1, 10, 7,4, 5, 9]

head,*_ = items
print(head,_)
def sm(li):
head,*tail = li
return head + sum(tail) if tail else head

print(sm(items))


保留最后N个历史记录使用deque双端队列

from collections import deque

q = deque(maxlen=3)
q.append(1)
q.append(2)
q.append(3)
print(q)
q.pop()
q.append(4)
print(q)
q.append(5)
q.popleft()
print(q)

实现一个优先级队列

class PriorityDeque:

def __init__(self):
self._deque = []
self._index = 0

def push(self,item,priority):
heapq.heappush(self._deque,(-priority,self._index,item))
self._index += 1

def pop(self):
return heapq.heappop(self._deque)[-1]


class Item:

def __init__(self,name):
self.name = name

def __repr__(self):
return 'Item({!r})'.format(self.name)


q = PriorityDeque()
q.push(Item('foo'),1)
q.push(Item('bar'),2)
q.push(Item('hello'),5)
q.push(Item('world'),1)
print(q._deque)
#[(-5, 2, Item('hello')), (-1, 0, Item('foo')), (-2, 1, Item('bar')), (-1, 3, Item('world'))]
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())
#队列包含了一个 (-priority, index, item) 的元组。 优先级为负数的目的
#是使得元素按照优先级从高到低排序。 这个跟普通的按优先级从低到高排序的堆排序恰巧相反

#index 变量的作用是保证同等优先级元素的正确排序。 通过保存一个不断增加的 index
#下标变量,可以确保元素安装它们插入的顺序排序。 而且, index 变量也在相同优先级
#元素比较的时候起到重要作用。

#如果不加入index下标的话遇到2个优先级一样的元素进行比较就会出错

#python在进行元组比较的时候如果第一个值的大小已经确定了就不会比较第2个值


字典中的键映射多个值

from collections import defaultdict

d = defaultdict(list)

d['a'].append(1)
d['a'].append(2)
d['a'].append(3)
print(d)
#defaultdict(<class 'list'>, {'a': [1, 2, 3]})

d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['a'].add(3)
print(d)
#defaultdict(<class 'set'>, {'a': {1, 2, 3}})

#你可以很方便的使用 collections 模块中的 defaultdict 来构造这样的字典。
#defaultdict 的一个特征是它会自动初始化每个 key 刚开始对应的值,所以你只需要关
#注添加元素操作了。

#也可以使用
d = {}
d.setdefault('a',[]).append(1)
d.setdefault('a',[]).append(2)
d.setdefault('a',[]).append(3)
print(d)
#{'a': [1, 2, 3]}

#自己实现一个多值映射字典
d = {}
for key,value in pairs:
if key not in d:
d[key] = []
d[key].append(value)

#使用defaultdict实现
d = defaultdict(list)
for key,value in pairs:
d[key].append(value)


字典排序

from collections import OrderedDict

def ordered_dict():
d = OrderedDict()
d['1'] = 1
d['2'] = 2
d['3'] = 3
d['4'] = 4
print(d)
#OrderedDict([('1', 1), ('2', 2), ('3', 3), ('4', 4)])

ordered_dict()

#使用有序的字典实现字典排序,有序的字典内部维护了一个双向链表占用空间是普通字典的2倍
#如果使用有序字典存储较大的元素就需要考虑空间占用问题


字典的运算

prices = {'ACME': 45.23,'AAPL': 612.78,'IBM': 205.55,'HPQ': 37.20,'FB': 10.7}

print(list(zip(prices.values(),prices.keys())))
#[(45.23, 'ACME'), (612.78, 'AAPL'), (205.55, 'IBM'), (37.2, 'HPQ'), (10.7, 'FB')]
print(sorted(zip(prices.values(),prices.keys())))
#[(10.7, 'FB'), (37.2, 'HPQ'), (45.23, 'ACME'), (205.55, 'IBM'), (612.78, 'AAPL')]
print(min(zip(prices.values(),prices.keys())))
#(10.7, 'FB')
print(max(zip(prices.values(),prices.keys())))
#(612.78, 'AAPL')
#使用zip函数反转字典的key value进行大小比较和排序


查找两字典的相同点

a = {'x': 1, 'y': 2, 'z': 3}
b = {'w': 10, 'x': 11, 'y': 2}

print(a.keys() & b.keys())
#{'x', 'y'}
print(a.keys() - b.keys())
#{'z'}
print(a.items() & b.items())
#{('y', 2)}
#字典的keys和items支持集合的交集,并集,差集运算
c = {key:a[key] for key in a.keys() - {'w', 'z'}}
print(c)
#{'x': 1, 'y': 2}
#也可以利用这个特性排除字典中的元素,values()方法不能保证元素的值相同所以不支持集合操作



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

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)


a = [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
b = [1, 5, 2, 1, 9, 1, 5, 10]

print(list(dedupe(b)))
# [1, 5, 2, 9, 10]
print(list(dedupe(a, key=lambda b: (b['x'], b['y']))))
# [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
print(list(dedupe(a, key=lambda b: (b['x']))))
# [{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]

# 使用类似sorted,min,max的技术构建函数使用上面的生成器函数可以让set中的元素有序


命名切片

record = '....................100 .......513.25 ..........'
SHARES = slice(20, 23)
PRICE = slice(31, 37)
print(record[SHARES], record[PRICE])
# 100 513.25
s = slice(5, 50, 2)
print(s.start)
#5
print(s.stop)
#50
print(s.step)
#2
s = 'HelloWorld'
a = slice(5, 10, 2)
print(a.indices(len(s)))
#(5, 10, 2)
for i in range(*a.indices(len(s))):
print(s[i],end=' ')
#W r d
#indices方法把所有值都会被合适的缩小以满足边界限
制, 从而使用的时候避免出现 IndexError 异常


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

from collections import Counter


words = [
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the',
'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into',
'my', 'eyes', "you're", 'under'
]

word_count = Counter(words)

# print(word_count.most_common(3))
# [('eyes', 8), ('the', 5), ('look', 4)]
# 输出出现频率最高的3个单词

morewords = ['why','are','you','not','looking','in','my','eyes']
#实现手动计数
# for word in morewords:
# word_count[word] += 1

#或者使用update方法
word_count.update(morewords)

# print(word_count)
Counter({'eyes': 9, 'the': 5, 'look': 4, 'my': 4, 'into': 3, 'not': 2, 'around': 2, "don't": 1, "you're": 1, 'under': 1, 'why': 1, 'are': 1, 'you': 1, 'looking': 1, 'in': 1})

#$Counter 实例一个鲜为人知的特性是它们可以很容易的跟数学运算操作相结合

a = Counter(words)
b = Counter(morewords)

c = a + b
d = a - b
print(c)
Counter({'eyes': 9, 'the': 5, 'look': 4, 'my': 4, 'into': 3, 'not': 2, 'around': 2, "don't": 1, "you're": 1, 'under': 1, 'why': 1, 'are': 1, 'you': 1, 'looking': 1, 'in': 1})
print(d)
Counter({'eyes': 7, 'the': 5, 'look': 4, 'into': 3, 'my': 2, 'around': 2, "don't": 1, "you're": 1, 'under': 1})


通过某个关键字排序一个字典列表
 
rows = [
{'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
{'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]

from operator import itemgetter

print(sorted(rows, key=itemgetter('fname')))

# [{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}]

print(sorted(rows, key=itemgetter('uid')))

# [{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}]

# 使用operator模块的itemgetter对数据库取出的值进行排序允许速度要比使用lambda函数排序速度快一些同时也支持min,max比较操作

print(sorted(rows, key=lambda a: a['fname']))
print(sorted(rows, key=lambda a: a['uid']))

# 也支持多个值的排序
print(sorted(rows, key=itemgetter('uid', 'lname')))
# [{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}]
print(min(rows, key=itemgetter('uid')))
# {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}
print(max(rows, key=itemgetter('uid')))
# {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}

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

class User:

def __init__(self,user_id):
self.user_id = user_id

def __repr__(self):
return 'User({})'.format(self.user_id)

users = [User(23),User(3), User(99)]

print(sorted(users,key=lambda a:a.user_id))
#[User(3), User(23), User(99)]
from operator import attrgetter

print(sorted(users,key=attrgetter('user_id')))
#[User(3), User(23), User(99)]
#operator模块的attrgetter可以对序列类型的数据进行排序itemgetter可以对字典类型的数据进行排序
print(min(users,key=attrgetter('user_id')))
#User(3)
#取出序列的最小值
print(max(users,key=attrgetter('user_id')))
#User(99)
#取出序列的最大值



通过某个字段将记录分组

rows = [
{'address': '5412 NCLARK', 'date': '07/01/2012'},
{'address': '5148 NCLARK', 'date': '07/04/2012'},
{'address': '5800 E58TH', 'date': '07/02/2012'},
{'address': '2122 NCLARK', 'date': '07/03/2012'},
{'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'},
{'address': '1060 WADDISON', 'date': '07/02/2012'},
{'address': '4801 NBROADWAY', 'date': '07/01/2012'},
{'address': '1039 WGRANVILLE', 'date': '07/04/2012'},
]

from itertools import groupby
from operator import itemgetter

# groupby只对已经排序好的数据有用

"""
groupby() 函数扫描整个序列并且查找连续相同值(或者根据指定key函数返回值相同)的元
素序列。 在每次迭代的时候,它会返回一个值和一个迭代器对象, 这个迭代器对象可以
生成元素值全部等于上面那个值的组中所有对象
"""

s = sorted(rows, key=itemgetter('date'))
# [{'address': '5412 NCLARK', 'date': '07/01/2012'},
# {'address': '4801 NBROADWAY', 'date': '07/01/2012'},
# {'address': '5800 E58TH', 'date': '07/02/2012'},
# {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'},
# {'address': '1060 WADDISON', 'date': '07/02/2012'},
# {'address': '2122 NCLARK', 'date': '07/03/2012'},
# {'address': '5148 NCLARK', 'date': '07/04/2012'},
# {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}]
for date, item in groupby(s, key=itemgetter('date')):
print(date)
#item返回一个可迭代的对象
for i in item:
print(' ', i)

"""
07/01/2012
{'address': '5412 NCLARK', 'date': '07/01/2012'}
{'address': '4801 NBROADWAY', 'date': '07/01/2012'}
07/02/2012
{'address': '5800 E58TH', 'date': '07/02/2012'}
{'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}
{'address': '1060 WADDISON', 'date': '07/02/2012'}
07/03/2012
{'address': '2122 NCLARK', 'date': '07/03/2012'}
07/04/2012
{'address': '5148 NCLARK', 'date': '07/04/2012'}
{'address': '1039 WGRANVILLE', 'date': '07/04/2012'}
"""
 
from collections import defaultdict

d = defaultdict(list)
for i in rows:
d[i['date']].append(i)

print(d)

defaultdict(<class 'list'>, {'07/01/2012': [{'address': '5412 NCLARK', 'date': '07/01/2012'}, {'address': '4801 NBROADWAY', 'date': '07/01/2012'}], '07/04/2012': [{'address': '5148 NCLARK', 'date': '07/04/2012'}, {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}], '07/02/2012': [{'address': '5800 E58TH', 'date': '07/02/2012'}, {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 WADDISON', 'date': '07/02/2012'}], '07/03/2012': [{'address': '2122 NCLARK', 'date': '07/03/2012'}]})

#如果不在乎排序的顺序可以使用defaultdict进行排序


过滤序列元素

# 1.使用列表推导式
mylist = [1, 4, -5, 10, -7, 2, 3, -1]

s = [i for i in mylist if i < 0 ]
print(s)
#[-5, -7, -1]
s = [i for i in mylist if i > 0 ]
print(s)
#[1, 4, 10, 2, 3]

#2.如果数据比较大的时候可以使用生成器表达式
s = (i for i in mylist if i > 0)
print(list(s))
#[1, 4, 10, 2, 3]

#3.处理比较复杂的需要用到异常处理的数据列表推导式就不能胜任了需要使用filter函数
values = ['1', '2', '-3', '-', '4', 'N/A', '5']

def is_int(values):
try:
val = int(values)
return True
except ValueError:
return False

print(list(filter(is_int,values)))

#['1', '2', '-3', '4', '5']

#替换不满足条件的值值
s = [i if i<0 else 0 for i in mylist]
print(s)
#[0, 0, -5, 0, -7, 0, 0, -1]
s = [i if i>0 else 0 for i in mylist]
print(s)
#[1, 4, 0, 10, 0, 2, 3, 0]
"""
另外一个值得关注的过滤工具就是 itertools.compress() , 它以一个 iterable 对象和一
个相对应的 Boolean 选择器序列作为输入参数。 然后输出 iterable 对象中对应选择器为
True 的元素。 当你需要用另外一个相关联的序列来过滤某个序列的时候,这个函数是非
常有用的。
"""

addresses = [
'5412 N CLARK',
'5148 N CLARK',
'5800 E 58TH',
'2122 N CLARK'
'5645 N RAVENSWOOD',
'1060 W ADDISON',
'4801 N BROADWAY',
'1039 W GRANVILLE',
]

counts = [ 0, 3, 10, 4,1,7, 6, 1]

from itertools import compress

more5 = [n > 5 for n in counts]
print(more5)
#[False, False, True, False, False, True, True, False]
print(list(compress(addresses,more5)))
#['5800 E 58TH', '4801 N BROADWAY', '1039 W GRANVILLE']

#这里的关键点在于先创建一个 Boolean 序列,指示哪些元素符合条件。 然后 compress()
#函数根据这个序列去选择输出对应位置为 True 的元素。


从字典中提取子集

prices = {
'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
}

d = {key:value for key,value in prices.items() if value > 200}
print(d)
#{'AAPL': 612.78, 'IBM': 205.55}
tech_names = {'AAPL', 'IBM', 'HPQ', 'MSFT'}
s = {key:value for key, value in prices.items() if key in tech_names}
print(s)
#{'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2}
#也可以使用集合运算方式但是速度会比上一种方式慢1.6倍
a = {key: prices[key] for key in prices.keys() & tech_names}
print(a)
#{'HPQ': 37.2, 'AAPL': 612.78, 'IBM': 205.55}


映射名称到序列元素

from collections import namedtuple

# Stock = namedtuple('Stock',['name','share','price'])
#
# s = Stock('新疆','100','123.5')
#
# print(s)
# #命名元组是不可更改的
# #Stock(name='新疆', share='100', price='123.5')
# c = s._replace(share=75)
# #使用_replace可以重新生成一个命名元组
# print(c)
# Stock(name='新疆', share=75, price='123.5')

"""
_replace() 方法还有一个很有用的特性就是当你的命名元组拥有可选或者缺失字段时
候, 它是一个非常方便的填充数据的方法。 你可以先创建一个包含缺省值的原型元组,
然后使用 _replace() 方法创建新的值被更新过的实例。
"""
Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])

stock_prototype = Stock('', 0.0, 0.0, None, None)
print(stock_prototype)
#Stock(name='', shares=0.0, price=0.0, date=None, time=None)

def dict_to_stock(s):
return stock_prototype._replace(**s)

a = {'name': 'ACME', 'shares': 100, 'price': 123.45}

print(dict_to_stock(a))
#Stock(name='ACME', shares=100, price=123.45, date=None, time=None)

b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'}

print(dict_to_stock(b))
#Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)


转换并同时计算数据
 
portfolio = [
{'name':'GOOG', 'shares': 50},
{'name':'YHOO', 'shares': 75},
{'name':'AOL', 'shares': 20},
{'name':'SCOX', 'shares': 65}
]

min_shares = min(i['shares'] for i in portfolio)
#更加优雅的实现方式,使用生成器表达式,省略了括号
print(min_shares)
#20


合并多个字典或映射

a ={'x': 1, 'z': 3 }
b ={'y': 2, 'z': 4}

from collections import ChainMap

"""
个 ChainMap 接受多个字典并将它们在逻辑上变为一个字典。 然后,这些字典并不是真
的合并在一起了, ChainMap 类只是在内部创建了一个容纳这些字典的列表 并重新定义了
一些常见的字典操作来遍历这个列表。大部分字典操作都是可以正常使用的
"""

c = ChainMap(a,b)
print(c)

#ChainMap({'x': 1, 'z': 3}, {'y': 2, 'z': 4})

print(c['y'])
#2
print(list(c.keys()))
#['y', 'z', 'x']
print(list(c.items()))
#[('y', 2), ('z', 3), ('x', 1)]
#ChainMap不会创建一个新的字典使用的是原来的字典的映射,修改2个字典共同存在的key的值总是作用在第一个字典上
c['x'] = 10
print(c)
#ChainMap({'x': 10, 'z': 3}, {'y': 2, 'z': 4})
del c['x']
print(c)
#ChainMap({'z': 3}, {'y': 2, 'z': 4})

update方法也可以合并多个字典但是会新生成一个字典对象修改字典的value不会作用到原来的对象上
c = dict(b)
c.update(a)
print(c)
#{'y': 2, 'z': 3, 'x': 1}
c['x'] = 10
print(c)
#{'y': 2, 'z': 3, 'x': 10}
print(a)
#{'x': 1, 'z': 3}


猜你喜欢

转载自www.cnblogs.com/louzi/p/10365998.html