python里的collections模块

collections模块里提供了一些特殊功能的容器,这里罗列一下:

namedtuple

如果不想一切都用dict,但又不想定义各种类,那么就可以使用namedtuple,有点像定义了一个C语言的结构体。感觉不太常用,但内部实现原理却很有趣,其实是通过模板生成定义代码,再使用exec()执行,感觉频繁使用会有性能问题。

Point = namedtuple('Point', ['x', 'y'])
p = Point(100, 50)
p.x ==> 100
p[0] ==> 100 #注意因为是tuple子类,所以也支持tuple的操作

deque

是由解释器内部的_collections.deque实现的,所以它性能更佳,如果用作stack来测试比list要快10%~15%。因为是双向队列,所以它可以从两端append和pop,可以像list那样当stack用,也可以当queue用。当然没有deque,list也能非常容易的实现stack和queue的功能,deque的主要区别在:

  • C语言实现,性能更好
  • 可以指定队列长度,达到容量后,之前append的元素自动覆盖
  • 两端操作,即append与appendleft,pop和popleft
q = deque([], 10)
for i in range(20):
    q.append(i)
q ==> deque([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])

特别注意:我们也可以使用list.pop(0)和list.insert(0, val)来实现popleft()和appendleft()一样的效果,但实际都有大量的数据拷贝操作,list.pop(index)只是删除index位置的元素,并返回它,所以在用作queue时,list和deque有巨大的性能差异!!!当然如果随机取,deque就慢得多,这非常容易理解,其实就是双链表和数组两种数据结构的差别。

q1 = []
t1 = time()
for i in range(1000):
    for i in range(10000):
        q1.append(i)
    for i in range(10000):
        q1.pop(0)
t2 = time()
print(t2-t1)  ==> 14.45s

q2 = deque()
t1 = time()
for i in range(1000):
    for i in range(10000):
        q2.append(i)
    for i in range(10000):
        q2.popleft()
t2 = time()
print(t2-t1)  ==> 1.96s

ChainMap

将多个dict链在一块,如果添加、删除、修改时,就只针对链中的第一个dict,但如果查询时,就依次查询链中的每个dict,直到查询到所需要的key。感觉不太常用。

Counter

感觉非常有用!它跟itertools.groupby()不同,它只是统计各元素出现的次数,它继承至dict,但它的update方法并不是更新操作,而是将统计结果加和,还有一个相减的函数为subtract()。

c = Counter('abcabcd')
c ==> Counter({'a': 2, 'b': 2, 'c': 2, 'd': 1})
c.update('abc')
c ==> Counter({'a': 3, 'b': 3, 'c': 3, 'd': 1})
c.update({'d': 99})
c ==> Counter({'a': 3, 'b': 3, 'c': 3, 'd': 100})
c.most_common(1) ==> [('d', 100)]

OrderedDict

这个就非常常见和常用了,就像php里的array,即是字典,同时也维护先后次序。注意它也是由内部_collections提供。

OrderedDict([
    ('name', 'zhangsan'),
    ('age', 24),
    ('school', 'hust'),
])

defaultdict

理论上应该很常用,不过好像实际用的不多,主要是仅仅只省了个判断语句而已。让人意外的是内部实现居然也是由C模块_collections提供。

d = defaultdict(int)
d['abc'] ==> 0

对于一种特别常见的场景,就是每个元素都是一个list,比如group by操作,python文档里特别提到使用defaultdict会比使用dict.setdefault(k, []).append()更simpler且更faster,实测发现确实要快25%~30%:

d1 = {}
t1 = time()
for i in range(100):
    for j in range(10000):
        for k in range(10):
            d1.setdefault(j, []).append(k)
t2 = time()
print(t2-t1)  ==> 2.8s

d2 = defaultdict(list)
t1 = time()
for i in range(100):
    for j in range(10000):
        for k in range(10):
            d2[j].append(k)
t2 = time()
print(t2-t1)   ==> 1.95s

综上,奇怪的一点是为啥这些类名有的全小写,而有的者是驼峰。

发表于 2020年03月27日 17:24   修改于 2020年03月27日 17:45   评论:0   阅读:2010  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo