|
详解Python中非常好用的计数器CounterCounter简介Counter是Python内置模块collections中的一个计数器工具,可以方便快捷地计数。Counter是字典dict的子类,用于计数可哈希(hashable)对象。(Python中实现了魔法方法__hash__的对象是hashable对象,关于可哈希和不可哈希,可以自行搜索了解,后面有时间我可以再专门写文章详细介绍)Counter是一个多项集,元素被存储为字典的键,元素的计数被存储为字典的值。计数可以是任何整数,包括零或负数。(甚至也可以是小数,不过本文不对此展开讨论。)Counter计数器的优点:用法简单:它可以通过一个可迭代对象(iterable)来初始化,用一个映射(mapping)对象(包括Counter本身)来初始化,用键值对来初始化,或者直接创建一个空的Counter实例。访问不存在的元素不报错:Counter对象的接口类似于字典,不同的是,如果查询的键不在Counter中,它会返回0,而不是抛出KeyError异常。相当于对任意键都有一个默认值0,这可以在一些遍历搜索的代码中避免程序报错。(严格来说,不报错有利有弊,不过在计数这种场景利大于弊,程序设计团队肯定也权衡过。)支持的方法很多:作为dict的子类,Counter继承了dict的大部分方法,此外还实现了一些非常便利的方法。Counter也可以转换成列表、集合、字典等Python内置数据类型,从而使用这些数据类型的方法,如切片操作等。接下来,本文将详细对Counter的各种特性和功能进行逐一介绍。计数器功能实现假设要统计一个列表[1,2,3,4,5,1,2,3]中各元素出现的次数,可以利用字典,用如下代码实现。#coding=utf-8nums=[1,2,3,4,5,1,2,3]result=dict()forninnums:ifnnotinresult:result[n]=1else:result[n]+=1print(result)123456789'运行运行Output:{1:2,2:2,3:2,4:1,5:1}1上面的代码中我们自己新建了一个字典,将待计数的列表nums中的元素作为字典的键,遍历nums,对每个元素进行计数。Counter的计数功能就类似于上面的代码,创建一个Counter对象即可完成计数。fromcollectionsimportCounternums=[1,2,3,4,5,1,2,3]print(Counter(nums))1234'运行运行Output:Counter({1:2,2:2,3:2,4:1,5:1})1可以看到,Counter封装了计数功能,只需要把待计数的数据传给Counter计数器,它即可立即返回一个计数器对象,完成对元素的计数。在需要计数时,可以直接调用Counter来使用,不用再自己写计数的代码。虽然自己写代码也并不复杂,但是更关键的是,Counter除了可以完成计数,还实现了一些非常好用的方法,方便我们对计数器进行更丰富的处理和解析。Counter初始化的几种方式#创建一个空计数器,然后用字典的方式给计数器添加值c=Counter()c['ai']=100print(c)#实例化时将可迭代对象传入Counterc1=Counter('pythoncode')print(c1)#实例化时将映射对象传入Counterc2=Counter({'a':5,'b':2,'c':1})print(c2)#实例化时将键值对传入Counterc3=Counter(A=1,B=2,C=5,D=6,E=7)print(c3)12345678910111213Output:Counter({'ai':100})Counter({'o':2,'p':1,'y':1,'t':1,'h':1,'n':1,'c':1,'d':1,'e':1})Counter({'a':5,'b':2,'c':1})Counter({'E':7,'D':6,'C':5,'B':2,'A':1})1234在实例化Counter时,可以传入可迭代对象(如字符串、列表),映射类型对象(如字典),键值对等。访问Counter中不存在的元素如果访问字典中不存在的键,会报错KeyError。例如:t_dict={'A':0,'K':3,'Q':3,'J':1}print(t_dict['KING'])12Output:Traceback(mostrecentcalllast):File"..\counter_demo.py",line36,inprint(t_dict['KING'])KeyError:'KING'1234在Counter中访问不存在的元素时,会返回0。(这相当于字典用setdefault(key,value)设置了默认值,通过魔法方法__missing__实现。)t_counter=Counter({'A':0,'K':3,'Q':3,'J':1})print(t_counter['KING'])12Output:01对于计数器中不存在的元素,返回的计数值为0,这比较符合生活常识,比报错KeyError友好很多,在一些代码中也有好处。此外,前面提到过,Counter中的计数还可以是负数,通常情况,计数不会有负数,不过负数也不是完全没有用处,对于一些特殊场景,如需要记录类似“欠账”的情况,负数也可以派上用场。这里还有一个注意点,Counter中某元素计数为0和Counter中不存在某元素,返回值都为0,代码处理(如访问元素的计数值、执行自增操作等)没有差别,但本质上并不一样,一种是Counter存在该元素,一种是Counter中不存在该元素。也就是说,如果把Counter中的一个元素的计数值改成0,并不代表从Counter中删除了该元素。t_counter['J']=0print(t_counter)print('J'int_counter)print('KING'int_counter)1234Output:Counter({'K':3,'Q':3,'A':0,'J':0})TrueFalse123要将元素从Counter中删除,跟字典删除方法相同,使用del关键字。delt_counter['J']print(t_counter)print('J'int_counter)123Output:Counter({'K':3,'Q':3,'A':0})False12Counter支持的方法Counter继承了字典的大部分方法,可以直接使用,如常用的keys()、values()、items(),还有copy()、clear()、pop()等等,就不逐一例举了。本章节主要介绍Counter支持的一些附加方法。elements():返回一个迭代器,其中每个元素都重复计数值所指定的次数。相同的元素会集中在一起,不同元素会按首次出现的顺序排列,如果一个元素的计数值小于1,elements()会将其忽略。c_counter=Counter(['red','pink','yellow','blue','yellow','blue','pink','blue'])print(c_counter.elements())print(list(c_counter.elements()))123Output:['red','pink','pink','yellow','yellow','blue','blue','blue']12most_common([n]):返回一个列表,包含计数值最大的n个元素及其计数,按计数值从高到低排序,如果计数值相等,按元素首次出现的顺序排列。如果n为None(不传值或传None),返回计数器中的所有元素。c_counter=Counter(['red','pink','yellow','blue','yellow','blue','pink','blue'])print(c_counter.most_common())print(c_counter.most_common(2))123Output:[('blue',3),('pink',2),('yellow',2),('red',1)][('blue',3),('pink',2)]12subtract([iterable-or-mapping]):减去一个可迭代对象或映射对象(包含Counter)中的元素(个数)。具体执行时将每个元素的计数值减去传入元素的计数值,输入输出都可以是0或负数。如果是不存在的元素,用0减。结果中元素的顺序按计数值从高到低排序,如果计数值相等,按元素首次出现的顺序排列。(可以发现,从文章开头,除elements()外,都是按这种顺序排序,本文后面的结果也都是按这种顺序。)c_counter=Counter(['red','pink','yellow','blue','yellow','blue','pink','blue'])print(c_counter)ls=['blue','yellow']c_counter.subtract(ls)#减一个列表print(c_counter)dt={'pink':1,'red':1,'green':1}c_counter.subtract(dt)#减一个字典print(c_counter)c2_counter=Counter({'blue':1,'yellow':2,'green':3})c_counter.subtract(c2_counter)#减另一个Counterprint(c_counter)1234567891011Output:Counter({'blue':3,'pink':2,'yellow':2,'red':1})Counter({'pink':2,'blue':2,'red':1,'yellow':1})Counter({'blue':2,'pink':1,'yellow':1,'red':0,'green':-1})Counter({'pink':1,'blue':1,'red':0,'yellow':-1,'green':-4})1234update([iterable-or-mapping]):加上一个可迭代对象或映射对象(包含Counter)中的元素(个数)。具体执行时将每个元素的计数值加上传入元素的计数值,输入和输出都可以是0或负数。c_counter.update(ls)#加一个列表print(c_counter)c_counter.update(dt)#加一个字典print(c_counter)c_counter.update(c2_counter)#加另一个Counterprint(c_counter)123456Output:Counter({'blue':2,'pink':1,'red':0,'yellow':0,'green':-4})Counter({'pink':2,'blue':2,'red':1,'yellow':0,'green':-3})Counter({'blue':3,'pink':2,'yellow':2,'red':1,'green':0})123Counter的subtract()和update()是一对互逆的方法,类似于字典的update(),不过Counter中是计数值的相加或相减,而非更新替换。此外,可迭代对象应当是一个元素序列,不能是一个(key,value)对组成的序列,那样会将整个序列当成一个整体元素处理。total():返回计数器中总的计数值,此方法从Python3.10开始支持,更早的版本不支持。c_counter=Counter(['red','pink','yellow','blue','yellow','blue','pink','blue'])print(c_counter)print(c_counter.total())123Output:Counter({'blue':3,'pink':2,'yellow':2,'red':1})812Counter的算术运算cnt1=Counter({'red':3,'green':2,'blue':1})cnt2=Counter({'red':1,'green':2,'blue':3})print(cnt1+cnt2)print(cnt1-cnt2)1234Output:Counter({'red':4,'green':4,'blue':4})Counter({'red':2})12两个Counter计数器相加或相减,会将计数器中各元素的计数值进行加减,得到一个新的Counter计数器。结果中只保留计数大于0的元素,也就是说算术运算前的计数器中可以有0或负数,但结果中不会保留0和负数。Counter的交并运算cnt1=Counter({'red':3,'green':2,'blue':1})cnt2=Counter({'red':1,'green':2,'blue':3})print(cnt1|cnt2)#并集print(cnt1&cnt2)#交集1234Output:Counter({'red':3,'blue':3,'green':2})Counter({'green':2,'red':1,'blue':1})12Counter的并集运算使用逻辑或的符号|,交集使用逻辑与的符号&。这里要注意,用Python的关键字or和and计算的结果不一样,or和and会依次判断两个Counter是否为True,做的是逻辑运算不是交并运算。计算并集时,结果取相同元素在两个Counter中计数值较大的计数,计算交集时,结果取相同元素在两个Counter中计数值较小的计数。Counter的比较运算cnt3=Counter({'red':20,'green':10,'blue':0})cnt4=Counter({'red':20,'green':10})print(cnt3==cnt4)print(cnt4,>=。在比较时,如果不存在的元素,会用计数0进行比较,所以上面的cnt3和cnt4相等。对于小于,只要Counter中有一个元素的计数值小于另一个Counter,其他元素的计数值可以相等,此时会返回True。大于同理。比较运算从Python3.10开始支持,更早的版本不支持。参考文档:https://docs.python.org/zh-cn/3/library/collections.html#counter-objects相关阅读:详解Python中的排列组合生成器📢欢迎点赞👍收藏⭐评论📝关注❤如有错误敬请指正!☟学Python,点击下方名片关注我。☟
|
|