Python Counter() 的实现
Table of Contents
collections.Counter 源码实现
Counter
的相关源码在lib下的collections.py里,本文所提及的源码是python2.7版本, 可参见github。
__init__
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.
'''
def __init__(*args, **kwds):
'''Create a new, empty Counter object. And if given, count elements
from an input iterable. Or, initialize the count from another mapping
of elements to their counts.
>>> c = Counter() # a new, empty counter
>>> c = Counter('gallahad') # a new counter from an iterable
>>> c = Counter({'a': 4, 'b': 2}) # a new counter from a mapping
>>> c = Counter(a=4, b=2) # a new counter from keyword args
'''
if not args:
raise TypeError("descriptor '__init__' of 'Counter' object "
"needs an argument")
self = args[0]
args = args[1:]
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
super(Counter, self).__init__()
self.update(*args, **kwds)
Counter
继承字典类来实现,初始化中对参数进行有效性校验,其中 args
接受除了 self
外最多一个未知参数。校验完成后调用自身的 update
方法来具体创建数据结构。
update
def update(*args, **kwds):
'''Like dict.update() but add counts instead of replacing them.
'''
if not args:
raise TypeError("descriptor 'update' of 'Counter' object "
"needs an argument")
self = args[0]
args = args[1:]
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
iterable = args[0] if args else None
if iterable is not None:
if isinstance(iterable, Mapping):
if self:
self_get = self.get
for elem, count in iterable.iteritems():
self[elem] = self_get(elem, 0) + count
else:
super(Counter, self).update(iterable) # fast path when counter is empty
else:
self_get = self.get
for elem in iterable:
self[elem] = self_get(elem, 0) + 1
if kwds:
self.update(kwds)
update
方法先检查参数,位置参数除了self外只允许有一个。然后对传入的参数进行判断,如果是以 Counter(a=1,b=2)
的方式调用的,这时候取出 kwds({'a':1,'b'=2})
再调用自身,将关键字参数转化为位置参数处理。
如果传入的位置参数是一个mapping类型的,对应于 Counter({'a':1,'b':2})
这样的方式调用,这种情况会判断self是否为空,在初始化状态下self总是空的,这边加上判断是因为update 方法不仅近在 __init__()
里调用,还可以这样调用:
x1 = collections.Counter({'a': 1, 'b': 2})
x2 = collections.Counter(a=1, b=2)
x1.update(x2) # Counter()类型 isinstance(iterable, Mapping) 也返回 True
# 或者这样调用
x1 = collections.Counter({'a': 1, 'b': 2})
x1.update('aab')
如果传入的不是一个mapping类型,那么会迭代该参数的每一项作为key添加到Counter中
most_common
def most_common(self, n=None):
'''List the n most common elements and their counts from the most
common to the least. If n is None, then list all element counts.
>>> Counter('abcdeabcdabcaba').most_common(3)
[('a', 5), ('b', 4), ('c', 3)]
'''
# Emulate Bag.sortedByCount from Smalltalk
if n is None:
return sorted(self.iteritems(), key=_itemgetter(1), reverse=True)
return _heapq.nlargest(n, self.iteritems(), key=_itemgetter(1))
如果调用 most_common
不指定参数n则默认返回全部(key, value)组成的列表,按照value降序排列。
itemgetter
这里用到了有趣的 itemgetter(代码里用了别名_itemgetter)
, 它是来自 operator
模块中的方法,可以从下面的代码感受一下:
# 例子来源python文档
# 举例:
After f = itemgetter(1), the call f(r) returns r[1].
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3]).
# 实现:
def itemgetter(*items):
if len(items) == 1:
item = items[0]
def g(obj):
return obj[item]
else:
def g(obj):
return tuple(obj[item] for item in items)
return g
# 常见用法:
>>> itemgetter(1)('ABCDEFG')
'B'
>>> itemgetter(1,3,5)('ABCDEFG')
('B', 'D', 'F')
>>> itemgetter(slice(2,None))('ABCDEFG')
'CDEFG'
>>> inventory = [('apple', 3), ('banana', 2), ('pear', 5), ('orange', 1)]
>>> getcount = itemgetter(1)
>>> map(getcount, inventory)
[3, 2, 5, 1]
>>> sorted(inventory, key=getcount)
[('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)]
heapquue
heap queue是“queue algorithm”算法的python实现,调用 _heapq.nlargest()
返回了根据每个value排序前n个大的(key, value)元组组成的列表。具体heap queue使用参见文档。
elements
elements
方法实现了按照value的数值重复返回key。它的实现很精妙,只有一行:
def elements(self):
'''Iterator over elements repeating each as many times as its count.
>>> c = Counter('ABCABC')
>>> sorted(c.elements())
['A', 'A', 'B', 'B', 'C', 'C']
'''
return _chain.from_iterable(_starmap(_repeat, self.iteritems()))
该实现里用到了 itertools
里的 repeat
starmap
chain
三个方法, 直接按照每项计数的次数重复返回每项内容,拼成一个列表。
repeat
repeat生成一个迭代器,根据第二个参数不停滴返回接受的第一个参数。直接看实现,很好理解, 类似实现如下:
def repeat(object, times=None):
# repeat(10, 3) --> 10 10 10
if times is None:
while True:
yield object
else:
for i in xrange(times):
yield object
starmap
starmap接受的第一个参数是一个函数,生成一个迭代器,不停滴将该函数以第二个参数传来的每一项为参数进行调用(说得抽象,看例子好理解),类似实现如下:
def starmap(function, iterable):
# starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
for args in iterable:
yield function(*args)
chain.from_iterable
chain.from_iterable 接受一个可迭代对象,返回一个迭代器,不停滴返回可迭代对象的每一项,类似实现如下:
def from_iterable(iterables):
# chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
for it in iterables:
for element in it:
yield element
substract
substract的实现和update实现很像,不同之处在counter()相同的项的计数相加改成了相减。
def subtract(*args, **kwds):
'''Like dict.update() but subtracts counts instead of replacing them.
Counts can be reduced below zero. Both the inputs and outputs are
allowed to contain zero and negative counts.
Source can be an iterable, a dictionary, or another Counter instance.
>>> c = Counter('which')
>>> c.subtract('witch') # subtract elements from another iterable
>>> c.subtract(Counter('watch')) # subtract elements from another counter
>>> c['h'] # 2 in which, minus 1 in witch, minus 1 in watch
0
>>> c['w'] # 1 in which, minus 1 in witch, minus 1 in watch
-1
'''
if not args:
raise TypeError("descriptor 'subtract' of 'Counter' object "
"needs an argument")
self = args[0]
args = args[1:]
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
iterable = args[0] if args else None
if iterable is not None:
self_get = self.get
if isinstance(iterable, Mapping):
for elem, count in iterable.items():
self[elem] = self_get(elem, 0) - count
else:
for elem in iterable:
self[elem] = self_get(elem, 0) - 1
if kwds:
self.subtract(kwds)
**
+, -, &, |
通过对 __add__
, __sub__
, __or__
, __and__
的定义,重写了 +, -, &, |
,实现了Counter间类似于集合的操作, 代码不难理解,值得注意的是,将非正的结果略去了:
def __add__(self, other):
'''Add counts from two counters.
>>> Counter('abbb') + Counter('bcc')
Counter({'b': 4, 'c': 2, 'a': 1})
'''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
newcount = count + other[elem]
if newcount > 0:
result[elem] = newcount
for elem, count in other.items():
if elem not in self and count > 0:
result[elem] = count
return result
def __sub__(self, other):
''' Subtract count, but keep only results with positive counts.
>>> Counter('abbbc') - Counter('bccd')
Counter({'b': 2, 'a': 1})
'''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
newcount = count - other[elem]
if newcount > 0:
result[elem] = newcount
for elem, count in other.items():
if elem not in self and count < 0:
result[elem] = 0 - count
return result
def __or__(self, other):
'''Union is the maximum of value in either of the input counters.
>>> Counter('abbb') | Counter('bcc')
Counter({'b': 3, 'c': 2, 'a': 1})
'''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
other_count = other[elem]
newcount = other_count if count < other_count else count
if newcount > 0:
result[elem] = newcount
for elem, count in other.items():
if elem not in self and count > 0:
result[elem] = count
return result
def __and__(self, other):
''' Intersection is the minimum of corresponding counts.
>>> Counter('abbb') & Counter('bcc')
Counter({'b': 1})
'''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
other_count = other[elem]
newcount = count if count < other_count else other_count
if newcount > 0:
result[elem] = newcount
return result
其它
# 当用Pickler序列化时,遇到不知道怎么序列化时,查找__reduce__方法
def __reduce__(self):
return self.__class__, (dict(self),)
# 重写删除方法,当Counter有这个key再删除,避免KeyError
def __delitem__(self, elem):
'Like dict.__delitem__() but does not raise KeyError for missing values.'
if elem in self:
super(Counter, self).__delitem__(elem)
# %s : String (converts any Python object using str()).
# %r : String (converts any Python object using repr()).
def __repr__(self):
if not self:
return '%s()' % self.__class__.__name__
items = ', '.join(map('%r: %r'.__mod__, self.most_common()))
return '%s({%s})' % (self.__class__.__name__, items)
@classmethod
def fromkeys(cls, iterable, v=None):
# There is no equivalent method for counters because setting v=1
# means that no element can have a count greater than one.
raise NotImplementedError(
'Counter.fromkeys() is undefined. Use Counter(iterable) instead.')
# 实现__missing__方法,当Couter['no_field'] => 0, 字典默认的__missing__ 方法不实现会报错(KeyError)
def __missing__(self, key):
'The count of elements not in the Counter is zero.'
# Needed so that self[missing_item] does not raise KeyError
return 0
总结
总体来说,Counter通过对内置字典类型的继承重写来的实现,比较简洁,逻辑也很清楚,从源码中可以学到很多标准库里提供的很多的不常见的方法的使用,可以使代码更加简洁,思路更加流畅。
Python Counter() 的实现的更多相关文章
- Python Counter class
Counter class https://docs.python.org/2/library/collections.html#collections.Counter # class collect ...
- 利用Python Counter快速计算出现次数topN的元素
需要用Python写一段代码,给定一堆关键词,返回出现次数最多的n个关键字. 第一反应是采用一个dict,key存储关键词,value存储出现次数,如此一次遍历即可得出所有不同关键词的出现次数,而后排 ...
- python counter、闭包、generator、解数学方程、异常
1.counter 2.闭包 3.generator 4.解数学方程 5.异常 1.python库——counter from collections import Counter breakfast ...
- Python Counter()计数工具
Table of Contents 1. class collections.Counter([iterable-or-mapping]) 1.1. 例子 1.2. 使用实例 2. To Be Con ...
- Python Counter
from collections import Counter print(Counter("宝宝今年特别喜欢王宝强")) # 计数 lst = ["jay", ...
- 【转】python Counter模块
>>> c = Counter() # 创建一个新的空counter >>> c = Counter('abcasdf') # 一个迭代对象生成的counter & ...
- Python入门(二,基础)
一,基本语法 Python标识符 在python里,标识符有字母.数字.下划线组成. 在python中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. python中的标识符是区分大 ...
- python 基础知识点整理 和详细应用
Python教程 Python是一种简单易学,功能强大的编程语言.它包含了高效的高级数据结构和简单而有效的方法,面向对象编程.Python优雅的语法,动态类型,以及它天然的解释能力,使其成为理想的语言 ...
- python学习第一天内容整理
.cnblogs_code { width: 500px } 一.python 的历史 (摘自百度百科,了解就ok) Python[1] (英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn ...
随机推荐
- The Stereo Action Dimension
Network MIDI on iOS - Part 1 This is an app I wrote to try out some ideas for networked MIDI on iP ...
- 利用AuthorizeAttribute属性简单避免 MVC 中的跨域攻击
跨域攻击---自然来路页面和目标页面不在同一个域下,所以直接判断来路域和当前自己的域就可以了. 可以广泛应用于表单提交,ajax调用或者某些不想让用户直接输入网址看到的页面 [csharp] view ...
- poj 1797 Heavy Transportation(Dijkstar变形)
http://poj.org/problem?id=1797 给定n个点,及m条边的最大负载,求顶点1到顶点n的最大载重量. 用Dijkstra算法解之,只是需要把“最短路”的定义稍微改变一下, A到 ...
- nyoj 119 士兵杀敌(三)(RMQ)
士兵杀敌(三) 时间限制:2000 ms | 内存限制:65535 KB 难度:5 描述 南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进 ...
- 【下有对策】verycd没有的资源有很多方法下载
由于国内专门从事假冒电驴官方欺骗中国人的verycd公司出品的冒牌官方电驴"阉割驴"目前已经阉割掉了搜索功能,请电驴爱好者们尽快更换正宗版电驴软件: 电骡--emule官方网站:h ...
- MEF(Managed Extensibility Framework)依赖注入学习
MSDN官方资料,并且微软还提供了SimpleCalculator sample学习样例 http://msdn.microsoft.com/en-us/library/dd460648(v=vs.1 ...
- spring+jpg环境下,spring实现文件下载web实现通用的文件下载方法
jar包复制到WEB-INF 文件夹lib下: commons-io-1.3.2.jar public static String download(HttpServletRequest reques ...
- Android学习过程中遇到的问题
1.使用在Activity布局之上重叠显示操作栏,第一次使用出现错误信息. 错误信息如下:java.lang.RuntimeException:Ubable to start activity Com ...
- PHP高级应用视频教程大全学习
php 是一种服务器端的,嵌入html的脚本语言.php区别其他像客户端java的地方是它的代码在服务器端执行.php能做什么?最低水平,php可以做任何其他cgi程序所能做的事,例如收集表格数据,生 ...
- js ajax上传图片到服务器
$("#up_goods_pic").on('change',function(){ var file = this.files[0]; var url = webkitURL.c ...