python 数据结构中被忽视的小技巧
一、一个包含N个元素的字符串、元组、序列、以及任何可迭代对象均可以拆分成N个单独的“变量”
1.字符串的拆分
1 #字符串 2 In [10]: s="abdefg" 3 In [11]: o, p, q, x, y, z = s
4 In [12]: q 5 Out[12]: 'd' 6 In [13]: x 7 Out[13]: 'e' 8 In [14]: z 9 Out[14]: 'g'
2.列表、元组、集合和可迭代对象的拆分
1 list_l = [1,2,3,4] 2 3 In [16]: tuple_l = [5,6,7,8] 4 5 In [17]: a,b,c,d = list_l 6 7 In [18]: b 8 Out[18]: 2 9 10 In [19]: c 11 Out[19]: 3 12 13 In [20]: d 14 Out[20]: 4 15 16 In [21]: m,n,o,p = tuple_l 17 18 In [22]: o 19 Out[22]: 7 20 21 In [23]: p 22 Out[23]: 8 23 24 25 d={'k1':'v1','k2':'v2'} 26 27 In [2]: d 28 Out[2]: {'k1': 'v1', 'k2': 'v2'} 29 30 In [3]: key1,key2 = d.keys() 31 32 In [4]: key1 33 Out[4]: 'k2' 34 35 In [5]: key2 36 Out[5]: 'k1'
一个综合的例子:
1 In [11]: data = [1,2,[3,4,6],(7,8,9),{'k1':'v1','k2':2}] 2 In [12]: a,b,li,tup,dic = data 3 In [13]: a 4 Out[13]: 1 5 In [14]: b 6 Out[14]: 2 7 In [15]: tup 8 Out[15]: (7, 8, 9) 9 In [16]: dic 10 Out[16]: {'k1': 'v1', 'k2': 2} 11 In [17]: li 12 Out[17]: [3, 4, 6]
问题1:如果想丢弃分解后的一些无用值怎么办?可以使用用不到的变量名"_"进行分割:
1 l=['money','money','no use','nouse','money'] 2 In [19]: a,b,_,_,e=l 3 In [20]: print(a,b) 4 money money 5 In [21]: print(a,b,e) 6 money money money
问题2:如果一个可迭代对象比较长,且大于要分割的数目怎么办?"*表达式"就该发挥作用了
l=range(1,100) In [24]: len(l) Out[24]: 99 In [25]: first,*p,last = l #p是一个列表[2,...,98] In [26]: print (first,len(p),last) 1 97 99
In [1]: l=range(1,100)
In [2]: *q,m,n = l
In [3]: print (len(q),m,n) #q是一个列表[1,...,96]
97 98 99
In [4]: x,y,*z=l
In [5]: print(x,y,len(z))
1 2 97
同样的,“*_”也可以作为看可以丢弃的无用的变量名
In [11]: l=range(1,100)
In [12]: first,*_,end = l
In [13]: print(first,end)
1 99
二、python标准库collections模块的常见应用
1. deque类,优点:可以在两端执行添加和弹出,并且效率更高
1 from collections import deque 2 # 创建一个总长度为5的deque对象 3 # 默认maxlen=None,无限长度 4 dq = deque(maxlen=5) 5 print(dq) 6 # out:deque([], maxlen=5) 7 # append、appendleft方法 8 dq.append(1) 9 print(dq) 10 # out:deque([1], maxlen=5) 11 dq.appendleft(2) 12 print(dq) 13 # out:deque([2, 1], maxlen=5) 14 # 插入序列列表、元组都可以 15 dq.extend([5,6]) 16 print(dq) 17 # out:deque([2, 1, 5, 6], maxlen=5) 18 dq.extendleft([7,8,9]) 19 print(dq) 20 # out:deque([9, 8, 7, 2, 1], maxlen=5) 21 # 从末尾pop出来一个元素:1 22 dq.pop() 23 print(dq) 24 # out:deque([9, 8, 7, 2], maxlen=5) 25 # 从开头出pop出来一个元素:9 26 dq.popleft() 27 print(dq) 28 # out:deque([8, 7, 2], maxlen=5) 29 # 复制一份,相互独立 30 dq_copy = dq.copy() 31 print(dq,dq_copy) 32 # out:deque([8, 7, 2], maxlen=5) deque([8, 7, 2], maxlen=5) 33 # dq.index(x)返回x在dq中的索引 34 print(dq.index(7)) 35 # out:1 36 # dq.count(x)返回dq中x的数目 37 print(dq.count(7)) 38 # out:1 39 # 返回容量 40 print(dq.maxlen) 41 # out:5 42 # dq.insert(index,x) 在index处插入x 43 dq.insert(0,110) 44 print(dq) 45 # out:deque([110, 8, 7, 2], maxlen=5) 46 # dq.remove(x)从dq中移除x 47 try: 48 dq.remove(99) 49 except Exception as e: 50 print(e) 51 # out:deque.remove(x): x not in deque 52 #dq.reverse(),转置 53 dq.reverse() 54 print(dq) 55 # out:deque([2, 7, 8, 110], maxlen=5) 56 for i in dq: 57 print(i) 58 ''' 59 2 60 7 61 8 62 110 63 ''' 64 # 清空所有元素 65 dq.clear() 66 print(dq) 67 # deque([], maxlen=5) 68 #^_^ over
2. defaultdict类,定义一个字典对象.优点:非常容易的实现一Key多Vallue的字典
例如;d={'a':[1,2,3,4],'b':[5,7]} ,s={'a':{2,3,4},'b':{4,5}}
实例化:d=defaultdict(list|set) 列表、集合根据需要设置,效果如上.(元素去重用set,保持元素顺序用list)
1 ''' 2 default一大优点就是创建实例的同时就已经初始化了一个可能会用到的值例如下列中的d1['a']、d2['a'] 3 ''' 4 In [5]: d1=dict() 5 In [6]: from collections import defaultdict 6 In [7]: d2=defaultdict(list) 7 In [8]: print(d1['a']) 8 --------------------------------------------------------------------------- 9 KeyError Traceback (most recent call last) 10 <ipython-input-8-1877cceef97a> in <module>() 11 ----> 1 print(d1['a']) 12 KeyError: 'a' 13 In [9]: print(d2['a']) 14 []
看一个我遇到的使用defaultdict的比较好的例子:
1 from collections import defaultdict 2 '''完整代码不贴了,用一个列表简化一下当时我的需求: 3 计算下面列表中的每一个元素split后的第二个元素是y的个数(即:string.split()[1]=='y'),并且与string.split()[0]一 一对应.
4 ''' 5 6 l=['a y','a n','a n','a n','a y','a y','a y''a y','b n','b n','b y','b y','b n','b y'] 7 8 d=defaultdict(list) 9 for i in l: 10 if i.split()[1]=='y': 11 d[i.split()[0]].append(1) #这一步完全忽视i.split()[0]是什么东西,就是干,换了dict()肯定要先定义一个key了。。。比如d=dict();d['a']=[]; 12 for k,v in d.items(): 13 print('{}:{}次'.format(k,len(v)))
--------------------------------------------
b:3次 #直到这里才知道都是么鬼,几个鬼
a:3次
3.OrderedDict类,看名字就跟顺序有关,是的,它的作用是让存入字典的k,v对保持插入时的顺序(数据量较大时需要考虑性能)
1 In [10]: from collections import OrderedDict 2 3 In [11]: d=OrderedDict() 4 5 In [12]: d['a']=1 6 7 In [13]: d['b']=2 8 9 In [14]: d['c']=3 10 11 In [15]: d['d']=4 12 13 In [16]: for k in d: 14 ....: print(k,d[k]) 15 ....: 16 ------------------------------------ 17 a 1 18 b 2 19 c 3 20 d 4
4.Counter类.能够简单快捷的返回序列中出现次数最多的元素和个数.
1 >>> l = [] 2 >>> for i in range(1,20): 3 ... l.append(chr(random.randint(97,100))) #这里我们并不知道都随机生成了什么字符列表,但是大致知道是[a-d] 4 ... 5 >>> list_count = Counter(l) #实例Counter对象 6 >>> top_three = list_count.most_common(3) #取出啊前三个出现次数最多的元素 7 >>> print (top_three) 8 [('a', 8), ('d', 4), ('b', 4)]
假如我们知道里面有一个字符(比如'a')出现了很多次,想要得到具体多少次呢?
>>> list_count['a'] 8 >>> list_count['b'] 4 >>> list_count['d'] 4 >>> list_count['c'] 3 >>> print(list_count['e']) 0 >>> print(l) ['a', 'd', 'a', 'd', 'a', 'b', 'a', 'a', 'd', 'b', 'd', 'a', 'a', 'a', 'c', 'c', 'c', 'b', 'b']
操作跟字典真是太像了,没错看一下Counter类的定义,以及其它用法
class Counter(dict):#没错,继承自字典。。。 '''Dict subclass for counting hashable items. Sometimes called a bag or multiset. Elements are stored as dictionary keys and their counts are stored as dictionary values. >>> c = Counter('abcdeabcdabcaba') # count elements from a string >>> c.most_common(3) # three most common elements [('a', 5), ('b', 4), ('c', 3)] >>> sorted(c) # list all unique elements ['a', 'b', 'c', 'd', 'e'] >>> ''.join(sorted(c.elements())) # list elements with repetitions 'aaaaabbbbcccdde' >>> sum(c.values()) # total of all counts 15 >>> c['a'] # count of letter 'a' 5 >>> for elem in 'shazam': # update counts from an iterable ... c[elem] += 1 # by adding 1 to each element's count >>> c['a'] # now there are seven 'a' 7 >>> del c['b'] # remove all 'b' >>> c['b'] # now there are zero 'b' 0 >>> d = Counter('simsalabim') # make another counter >>> c.update(d) # add in the second counter >>> c['a'] # now there are nine 'a' 9 >>> c.clear() # empty the counter >>> c Counter() Note: If a count is set to zero or reduced to zero, it will remain in the counter until the entry is deleted or the counter is cleared: >>> c = Counter('aaabbc') >>> c['b'] -= 2 # reduce the count of 'b' by two >>> c.most_common() # 'b' is still in, but its count is zero [('a', 3), ('c', 1), ('b', 0)] '''
三、通过公共键,对字典列表排序(sorted),最大值(max),最小值(min)
方法-1:使用operator.itemgetter
from operator import itemgetter l = [ {'name': '股票1','num': '000001','price': 13.7}, {'name': '股票2','num': '000002','price': 15.7}, {'name': '股票4','num': '000004','price': 16.7}, {'name': '股票3','num': '000003','price': 10.7} ] print(l) sort_by_bum = sorted(l,key=itemgetter('num')) sort_by_price = sorted(l,key=itemgetter('price')) print(sort_by_bum) print(sort_by_price) --------------------------- [{'name': '股票1', 'num': '000001', 'price': 13.7}, {'name': '股票2', 'num': '000002', 'price': 15.7}, {'name': '股票4', 'num': '000004', 'price': 16.7}, {'name': '股票3', 'num': '000003', 'price': 10.7}] [{'name': '股票1', 'num': '000001', 'price': 13.7}, {'name': '股票2', 'num': '000002', 'price': 15.7}, {'name': '股票3', 'num': '000003', 'price': 10.7}, {'name': '股票4', 'num': '000004', 'price': 16.7}] [{'name': '股票3', 'num': '000003', 'price': 10.7}, {'name': '股票1', 'num': '000001', 'price': 13.7}, {'name': '股票2', 'num': '000002', 'price': 15.7}, {'name': '股票4', 'num': '000004', 'price': 16.7}]
---------------------------------------------------
from operator import itemgetter
l = [
{'name': '股票1','num': '000001','price': 13.7},
{'name': '股票2','num': '000002','price': 15.7},
{'name': '股票3','num': '000003','price': 16.7},
{'name': '股票3','num': '000003','price': 10.7}
]
print(l)
sort_by_num_price = sorted(l,key=itemgetter('num','price'))
print(sort_by_num_price)
---------------------------
[{'price': 13.7, 'num': '000001', 'name': '股票1'}, {'price': 15.7, 'num': '000002', 'name': '股票2'}, {'price': 16.7, 'num': '000003', 'name': '股票4'}, {'price': 10.7, 'num': '000003', 'name': '股票3'}]
[{'price': 13.7, 'num': '000001', 'name': '股票1'}, {'price': 15.7, 'num': '000002', 'name': '股票2'}, {'price': 10.7, 'num': '000003', 'name': '股票3'}, {'price': 16.7, 'num': '000003', 'name': '股票4'}]
方法-2:区别仅仅在于key后面函数的实现方式.无论lambda,itemgetter都是定义了一个func
func(x) = lambda x:x['num'] ==>return x['num']
func = itemgetter('num') func(r)==>return r['num']
#使用匿名函数lambda l = [ {'name': '股票1','num': '000001','price': 13.7}, {'name': '股票2','num': '000002','price': 15.7}, {'name': '股票4','num': '000004','price': 16.7}, {'name': '股票3','num': '000003','price': 10.7} ] print(l) sort_by_bum = sorted(l,key=lambda x:x['num']) sort_by_price = sorted(l,key=lambda x:x['price']) print(sort_by_bum) print(sort_by_price) ------------------------- [{'name': '股票1', 'num': '000001', 'price': 13.7}, {'name': '股票2', 'num': '000002', 'price': 15.7}, {'name': '股票4', 'num': '000004', 'price': 16.7}, {'name': '股票3', 'num': '000003', 'price': 10.7}] [{'name': '股票1', 'num': '000001', 'price': 13.7}, {'name': '股票2', 'num': '000002', 'price': 15.7}, {'name': '股票3', 'num': '000003', 'price': 10.7}, {'name': '股票4', 'num': '000004', 'price': 16.7}] [{'name': '股票3', 'num': '000003', 'price': 10.7}, {'name': '股票1', 'num': '000001', 'price': 13.7}, {'name': '股票2', 'num': '000002', 'price': 15.7}, {'name': '股票4', 'num': '000004', 'price': 16.7}]
---------------------------------------------------分割线--
l = [
{'name': '股票1','num': '000001','price': 13.7},
{'name': '股票2','num': '000002','price': 15.7},
{'name': '股票3','num': '000003','price': 16.7},
{'name': '股票3','num': '000003','price': 10.7}
]
print(l)
sort_by_num_price = sorted(l,key=lambda x:(x['num'],x['price']))
print(sort_by_num_price)
---------------------------
[{'price': 13.7, 'num': '000001', 'name': '股票1'}, {'price': 15.7, 'num': '000002', 'name': '股票2'}, {'price': 16.7, 'num': '000003', 'name': '股票4'}, {'price': 10.7, 'num': '000003', 'name': '股票3'}]
[{'price': 13.7, 'num': '000001', 'name': '股票1'}, {'price': 15.7, 'num': '000002', 'name': '股票2'}, {'price': 10.7, 'num': '000003', 'name': '股票3'}, {'price': 16.7, 'num': '000003', 'name': '股票4'}]
最大值,最小值,max,min
from operator import itemgetter l = [ {'name': '股票1','num': '000001','price': 13.7}, {'name': '股票2','num': '000002','price': 15.7}, {'name': '股票4','num': '000004','price': 16.7}, {'name': '股票3','num': '000003','price': 10.7} ] max = max(l,key=itemgetter('price')) min = min(l,key=itemgetter('price')) print(max) print(min) ----------------------------- {'num': '000004', 'price': 16.7, 'name': '股票4'} {'num': '000003', 'price': 10.7, 'name': '股票3'}
四.高阶函数map/filter/sorted
class map(object)
| map(func, *iterables) --> map object
| Make an iterator that computes the function using arguments from
| each of the iterables. Stops when the shortest iterable is exhausted.
简单解释下,map有需要两个参数func和iterables.可以这么理解,func是你根据需求编写的一个函数,iterables是你需要操作的一个可迭代对象,他们的返回值是一个iterator(迭代器),有关迭代器的概念这里不多讲,不过暂时可以简单的记住迭代器可以通过list(iterator)或者set(iterator)生成一个列表或者集合.
举个比较经典的例子吧,把一个数列a = [1,2,3,3,4,5,6]中的所有元素平方后生成一个新的数列b:
>>> a = [1,2,3,4,5,67] >>> def func(x): ... return x*x ... >>> b=list(map(func,a)) >>> b [1, 4, 9, 16, 25, 4489] >>>
在这个例子中,假设你不清楚map用法也完全可以得到我们想要的结果b,不过那样的话你是不是至少用一个for循环呢,多谢几行代码呢?其实多写几行代码倒是没有什么,重点使用map起码能够给我们的代码增添一点点点的亮点。
class filter(object)
| filter(function or None, iterable) --> filter object
| Return an iterator yielding those items of iterable for which function(item)
| is true. If function is None, return the items that are true.
跟map一样需要两个参数func和可迭代对象,在filter执行过程中把iterable中的每一个元素迭代到func中,如果执行结果为真的把当前元素添加到迭代器中,当所有元素执行完后,返回迭代器.
看例子:
a=[4,5,6,7,8,9,0] >>> def dayu_5(x): ... if x>5: ... return True >>> list(filter(dayu_5,a)) [6, 7, 8, 9]
sorted(iterable, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
返回一个包含了所有可迭代对象,并且按照升序排列的一个新的列表
看例子:
>>> l=[2,34,5,61,2,4,6,89] >>> sorted(l) [-89, -6, 2, 2, 4, 5, 34, 61] >>> sorted(l,key=lambda x:abs(x)) [2, 2, 4, 5, -6, 34, 61, -89] >>> sorted(l,key=lambda x:abs(x),reverse=True) [-89, 61, 34, -6, 5, 4, 2, 2]
#三个例子分别使用了sorted的默认排序,key自定义排序,以及reverse翻转的功能.
key其实表示的一个函数,接着看下面的例子:
>>> def ff(x):
... return abs(x)
>>> sorted(l,key=ff,reverse=False)
[2, 2, 4, 5, -6, 34, 61, -89]
可以看到效果是一样的
五 heapq模块
nlargest()和nsmallest()
Help on function nlargest in module heapq:
nlargest(n, iterable, key=None)
Find the n largest elements in a dataset.
Equivalent to: sorted(iterable, key=key, reverse=True)[:n]
通过help函数可以看到,heapq.nlargest()作用为从可迭代对象iterable中,依据key的定义取出最大的n个值,其作用等于sorted(iterable, key=key, reverse=True)[:n]表达式
1 >>> l2=[(10,2),(9,5),(8,10),(7,6),(6,1)] 2 >>> l = [12,34,22,45,678,89,100,232,23] 3 >>> help(heapq.nlargest) 4 5 >>> l = [12,34,22,45,678,89,100,232,23] 6 >>> heapq.nlargest(5,l) 7 [678, 232, 100, 89, 45] 8 >>> l2=[(10,2),(9,5),(8,10),(7,6),(6,1)] 9 >>> heapq.nlargest(3,l2,key=lambda x:x[1]) 10 [(8, 10), (7, 6), (9, 5)] 11 >>> 12 >>> sorted(l,reverse=True)[:5] 13 [678, 232, 100, 89, 45] 14 >>>
Help on function nsmallest in module heapq:
nsmallest(n, iterable, key=None)
Find the n smallest elements in a dataset.
Equivalent to: sorted(iterable, key=key)[:n]
通过help帮助可以发现nsmallest()刚好和nlargest()作用相反
1 >>> heapq.nsmallest(5,l) 2 [12, 22, 23, 34, 45] 3 >>> sorted(l)[:5] 4 [12, 22, 23, 34, 45] 5 >>> heapq.nsmallest(3,l2,key=lambda x:x[1]) 6 [(6, 1), (10, 2), (9, 5)] 7 >>>
六 去除序列中的重复项并保持顺序不变
通常情况下我们经常用set给序列排序,但是往往原来的顺序会发生改变,如下面例子
>>> l=[] >>> for i in range(10): ... l.append(random.randint(1,4)) ... >>> list(set(l)) [1, 2, 3, 4] >>>
待续。。