数据结构与算法,讲述一些对于序列和字典的操作。主要借助collections来实现。
名词解释 可迭代对象 定义:可以一次一个地返回元素。在python中,能够放在for循环右侧的基本都是可迭代对象
代表:生成器,文件对象,集合,字典(遍历的是键)
序列 定义:有索引的可迭代对象
代表:列表,元组,字符串,范围(range)
collections 定义:python的标准库之一,提供了多种数据类型,如 defaultdict、deque、OrderedDict、Counter等
可哈希(hashable) 定义:如果一个对象有固定的哈希值,并且在其生命周期内这个号码绝对不会变,同时它能和其它正常对象比较是否相等,那就是可哈希的。
os (operating system) 它为我们提供了一种跨平台的方式,让我们编写的 Python 程序能够与底层的操作系统进行交互,比如操作文件、目录、环境变量和进程等
1.1 将序列分解为单独的变量 任何序列都可以通过赋值操作分解为与序列元素数相等的变量。
1 2 3 4 5 6 7 a = [1 ,2 ,3 ] b,c,d = a print (f"{b} \n{c} \n{d} " )
1.2 解压可迭代对象赋值给多个变量 通过在变量名称前加*可以起到类似于切片的效果。星号解压语法也可以用来分割长度不确定的序列。
1 2 3 4 5 6 7 8 9 10 information = ["enterprise" ,"10-03" ,"girl" ,"uss" ] name,*value = information print (f"{name} \n{value} \n" )*key,country = information print (f"{key} \n{country} \n" )
1.3 保留最后N个元素 正常情况下采用切片即可,但是对于动态流式的数据采用collections.deque更好。核心思想是定义一个数组,元素先进先出。
1 2 3 4 5 6 7 8 9 10 11 from collections import dequeanswer = deque(maxlen=10 ) for i in "yorktown" : answer.append(i) for i in "enterprise" : answer.append(i) print (answer)
1.4 查找最大或最小的N个元素 colllecions.heapq的nlargest()和nsmallest()可以完美解决这个问题。分别用于查找最大和最小元素。
1 2 3 4 5 import heapqa = "enterprise" print (heapq.nlargest(4 ,a))
1.5 实现优先级队列 借助heapq来实现。特点为列表中索引为0的元素永远最小。当数据规模是动态的时候吗,使用堆来满足获取极值的要求具有绝对的优势。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import heapqpq = [] heapq.heappush(pq, [2 , 'enterprise' ]) heapq.heappush(pq, [1 , 'yorktown' ]) heapq.heappush(pq, [3 , 'hornet' ]) print (heapq.heappop(pq)) print (heapq.heappop(pq)) print (heapq.heappop(pq))print (pq)
1.6 字典映射多个值 可以将value用序列来表示,也可以借助collections.default,本质是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 from collections import defaultdictship = defaultdict(set ) ship["yorktown" ].add("yorktown" ) ship["yorktown" ].add("enterprise" ) ship["yorktown" ].add("hornet" ) ship["lexington" ].add("lexington" ) ship["lexington" ].add("saratoga" ) print (ship)
1.7 字典排序 使用collections.QrderedDict可以在迭代操作中保持元素被插入时的顺序。(注:不是按大小重新排序)
1 2 3 4 5 6 7 8 9 10 from collections import OrderedDictship = OrderedDict() ship['yorktown' ] = 5 ship['enterprise' ] = 6 ship['hornet' ] = 7 ship['saratoga' ] = 3 print (ship)
1.8 字典的运算 对于求字典的最值和排序,需要先借助zip()函数将键和值反转过来。否则会作用于键而不是值。
1 2 3 4 5 6 7 8 9 10 11 12 13 from collections import OrderedDictship = OrderedDict() ship['yorktown' ] = 5 ship['enterprise' ] = 6 ship['hornet' ] = 7 ship['saratoga' ] = 3 answer = max (zip (ship.values(),ship.keys())) print (answer)sorted (zip (ship.values(),ship.keys()))
1.9 查找两字典的相同点 查找字典相同点的方法很多,但是无法直接查找相同的值,因为值不一定满足一一映射。
1 2 3 4 5 print (a.keys() & b.keys()) print (a.items() & b.items()) print (a.keys()-b.keys()) c = {key:a[key] for key in a.keys() - {'z' , 'w' }}
1.10 删除序列相同元素并保持顺序 定义一个集合,将序列的元素按顺序逐一放入
1 2 3 4 5 6 7 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)
1.11 命名切片 变量可以用切片来赋值
1 2 3 4 5 6 record = '....................100 .......513.25 ..........' SHARES = slice (20 , 23 ) PRICE = slice (31 , 37 ) cost = int (record[SHARES]) * float (record[PRICE])
1.12 序列中出现次数最多的元素 可以用collections.Counter解决
1 2 3 4 5 6 7 8 9 10 11 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' ] from collections import Counterword_counts = Counter(words) top_three = word_counts.most_common(3 ) print (top_three)
1.13 通过某个关键字排序一个字典列表 使用operator模块的itemgetter函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 itemgetterrows_by_fname = sorted (rows, key=itemgetter('fname' )) rows_by_uid = sorted (rows, key=itemgetter('uid' )) rows_by_lfname = sorted (rows, key=itemgetter('lname' ,'fname' )) print (rows_by_fname , end='\n\n' )print (rows_by_uid , end='\n\n' )print (rows_by_lfname )
1.14 排序不支持原生比较的对象 当定义了一个类,并创建了多个实例放在列表里,使用sorted()排序会报错,因为py不知道按照什么规则来比。解决方法有两种,使用operate.attrgetter,或者借助lamba专门定义一个函数来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class User : def __init__ (self, user_id ): self .user_id = user_id def __repr__ (self ): return 'User({})' .format (self .user_id) def sort_notcompare (): users = [User(23 ), User(3 ), User(99 )] print (users) print (sorted (users, key=lambda u: u.user_id),end='\n\n' ) sort_notcompare() from operator import attrgetterusers = [User(23 ), User(3 ), User(99 )] print (sorted (users, key=attrgetter('user_id' )))
1.15 通过某个字段将记录分组 使用itertools.groupby()实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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' }, ] from operator import itemgetterfrom itertools import groupbyrows.sort(key=itemgetter('date' )) example = groupby(rows, key=itemgetter('date' )) for date,items in example: print (date) for i in items: print (' ' ,i)
1.16 过滤序列元素 使用列表推导。对于复杂的过滤规则可以定义一个函数,并借助filter实现过滤。
1 2 3 4 5 6 7 8 9 10 11 12 values = ['1' , '2' , '-3' , '-' , '4' , 'N/A' , '5' ] def choose (e ): try : e = int (e) return True except ValueError : return False show = list (filter (choose, values)) print (show)
1.17 从字典中提取子集 使用字典推导
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 prices = { 'ACME' : 45.23 , 'AAPL' : 612.78 , 'IBM' : 205.55 , '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} print (p1,end='\n' )print (p2)
1.18 映射名称到序列元素 这个操作类似于定义一个结构体。
1 2 3 4 5 6 7 8 9 10 >>> from collections import namedtuple>>> Subscriber = namedtuple('Subscriber' , ['addr' , 'joined' ])>>> sub = Subscriber('jonesy@example.com' , '2012-10-19' )>>> subSubscriber(addr='jonesy@example.com' , joined='2012-10-19' ) >>> sub.addr'jonesy@example.com' >>> sub.joined'2012-10-19' >>>
1.19 转换并同时计算数据 比较好的解决方式是使用一个生成器表达式参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import osfiles = os.listdir('dirname' ) if any (name.endswith('.py' ) for name in files): print ('There be python!' ) else : print ('Sorry, no python.' ) s = ('ACME' , 50 , 123.45 ) print (',' .join(str (x) for x in s))portfolio = [ {'name' :'GOOG' , 'shares' : 50 }, {'name' :'YHOO' , 'shares' : 75 }, {'name' :'AOL' , 'shares' : 20 }, {'name' :'SCOX' , 'shares' : 65 } ] min_shares = min (s['shares' ] for s in portfolio)
1.20 合并多个字典或映射 使用collections.ChainMap,将b里面a不存在的键加入
1 2 3 4 5 6 7 8 a = {'x' : 1 , 'z' : 3 } b = {'y' : 2 , 'z' : 4 } from collections import ChainMapc = ChainMap(a,b) print (c['x' ]) print (c['y' ]) print (c['z' ])