在11-15小节中,讲述了为切片命名的技巧,使用counter类来统计list中各个元素的数量,以及itemgetter用于在对字典排序时类目的获取、attrgetter用于对对象排序时属性的获取,最后是使用groupby对排序后的list内容进行分组。接下来是这一章的最后五小节。
filter、compress实现对list的筛选
当然,在数据量非常小的情况下,我们使用list comprehension来实现筛选
1 | >> mylist = [1, 4, -5, 10, -7, 2, 3, -1] |
潜在的隐患就是当数据量较大是可能会一次生成太多的数据,这时可以使用iterator来逐次返回需要的数据
1 | >> pos = (n for n in mylist if n > 0) |
有时,list comprehension难以表达某些复杂的筛选条件,这是filter就派上用场了,使用filter初始化后的函数可以用于生成新的筛选后的列表
1 | values=['1','2','-3','-','4','N/A','5'] |
filter同样是生成的iterator,因此想要得到结果仍需要使用list()来将其变成真正的list.
discussion
list comprehension和generator是两种最直接简单的筛选列表的方式,其中list comprehension是即时生成的,甚至你可以选择替代某些不符合筛选要求的元素为某种东西。
1 | replaced=[n if n>0 else 0 for n in values] |
另外还有一种compress方法可以将筛选条件以值为boolean的list的形式传入,则会选出相应位置为true的元素。
1 | counts=[3,4,5,6,7,8] |
1.17筛选字典元素
使用dict comprehension同样也是十分方便的处理该问题的方法:
1 | prices={ |
也可以传入一系列的tuple来生成,但是这样的做法会比较慢
1 | p1=dict((key,value) for key,value in prices.items() if value>200) |
1.18 namedtuples
当你写了一段代码,通过位置来获取列表或者元祖中的元素,你会发现这段代码在理解上会存在问题,而且也会存在隐患,比如当你获取到的数据源发生变化时,你可能会到处找这样的代码来修改。
collections.namedtuples是一个将位置命名并返回新的数据结构的类,在处理上述情况时非常有用。
1 | >>from collections import namedtuple |
实例化的namedtuple通过tuple来保存信息,并支持大多数tuple的操作
1 | >>len(sub) |
多数情况下namedtuple被用来解决信息获取与位置的耦合关系。
1 | from collections import namedtuple |
discussion
可能你会问了,如果我传入namedtuple实例的元素数量不够怎么办呢,这就要提到_replace
方法,该方法接收字典类型的参数,可以将字典中对应的值与namedtuple中的值进行替换。
1 | from collections import namedtuple |
如果你只是需要一种能够高效地改变属性的数据结构,那么namedtuple并不是最好的选择,在类中定义__slots__
方法来实现是更好的选择。
1.19 得到结果的同时进行转换和筛选
problem
你需要对一系列数据进行某个reduction操作(eg.,sum()、min()、max()),但在得到结果之前需要对数据进行转换或者筛选。
solution
一个非常elegent的方法是使用generator-expression,例如
1 | >>nums=[1,2,3] |
其他几个例子:
1 | #判断某个目录下存在py文件 |
discussion
1.效率问题
在第一个例子中,sum(x**2 for x in nums)
与sum((x**2 for x in nums))
会得到相同的结果,事实上两者都是将generator-expr作为参数传进sum函数,但是前者语法上更加简洁,在此提醒,不推荐使用sum([x**2 for x in nums])
这种方式,因为会占据额外的内存,这点在数据量比较大的时候会非常明显。
2.key值
事实上reduction函数例如min、max会接收第二个参数作为key值,这可能在你想要使用generator时会有用。
1 | #最初的方式,直接得到数值 |
1.20 将多个映射表连接成一个
problem
你想要将多个映射表集中在一起进行某个操作,比如查询和筛选等。
solution
from collections import ChainMap
假设你有两个dict:a={'x':1,'z':3}
b={'y':2,'z':4}
现在你想要从这两个字典中查找某个键值,(先从a找,若没有,再从b找),最简单的方法是使用ChainMap:
1 | from collectiosn import ChainMap |
discussion
虽然ChainMap使得多个字典看起来像是在同一序列中,但其实并没有真正将这些字典合并,而是将这些字典的引用在内部保存为一个list,并重新定义了接口使得可以通过这个list来访问这些字典。大多数的字典操作都是有效的:
1 | >>len(c) |
如果同一个chainmap中的字典中相互之间有重复的键名,那么通过chainmap只能访问到第一次出现的哪个值。
ChainMap在操作程序中不同作用域的参数时,非常有用:
1 | >>values=ChainMap() |
可能你会说,不用chainmap也可以实现上述方法,那就是dict的update方法,比如:
1 | >>merged=dict(b) |
但是,这种方式要求使用两个不同的字典(才能保持访问的有效性),并且对原始dict的修改也无法反映到merged中。
1 | >>a['x']=3 |
由于ChainMap访问的是原始dict,因此不会受上述限制
1 | >>merged=ChainMap(a,b) |