collections是Python内建的一个集合模块,提供了许多有用的集合类。
collections模块
collections模块自Python 2.4版本开始被引入,包含了dict、set、list、tuple以外的一些特殊的容器类型,分别是:
namedtuple(): 命名元祖 生成可以使用名字来访问元素内容的tuple子类
deque:双端队列(list),可以快速的从另外一侧追加和推出对象
Counter:计数器,主要用来计数
OrderedDict: 有序字典
defaultdict: 带有默认值的字典
文档参见:http://docs.python.org/2/library/collections.html。
1、namedtuple()
In [8]: from collections import namedtuple
In [9]: help(namedtuple)
Help on function namedtuple in module collections:
namedtuple(typename, field_names, verbose=False, rename=False) #类名,迭代器
Returns a new subclass of tuple with named fields.
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Point.__doc__ # docstring for the new class
'Point(x, y)'
>>> p = Point(11, y=22) # instantiate with positional args or keywords
>>> p[0] + p[1] # indexable like a plain tuple
33
>>> x, y = p # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessable by name
33
>>> d = p._asdict() # convert to a dictionary
>>> d['x']
11
>>> Point(**d) # convert from a dictionary
Point(x=11, y=22)
>>> p._replace(x=100) # _replace() is like str.replace() but targets
named fields
Point(x=100, y=22)
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。
实际案例:
需要从一个csv文件里读入数据,csv文件的第一行是字段名称,希望读入的数据可以根据字段名访问
[root@Node3 tmp]# cat contatcts.csv #csv文件
name,gender,email,phone,sn
comyn,man,xj@anyfish.com,15711112222,15034
comyn1,man,xj1@anyfish.com,157111122221,150341
comyn2,man,xj2@anyfish.com,157111122222,150342
comyn,man,xj@anyfish.com,15711112222,15034
comyn,man,xj@anyfish.com,15711112222,15034
comyn,man,xj@anyfish.com,15711112222,15034
comyn,man,xj@anyfish.com,15711112222,15034
comyn,man,xj@anyfish.com,15711112222,15034
comyn,man,xj@anyfish.com,15711112222,15034
comyn,man,xj@anyfish.com,15711112222,15034
解决方案:
In [92]: import csv
In [93]: from collections import namedtuple
In [94]: contacts = list()
In [95]: contacts
Out[95]: []
In [98]: reader = csv.reader(open("/tmp/contatcts.csv"))
In [99]: type(reader)
Out[99]: _csv.reader
In [100]: reader.
reader.dialect reader.line_num reader.next
In [100]: reader.next()
Out[100]: ['name', 'gender', 'email', 'phone', 'sn']
In [101]: reader.next()
Out[101]: ['comyn', 'man', 'xj@anyfish.com', '15711112222', '15034']
In [102]: reader.next()
Out[102]: ['comyn1', 'man', 'xj1@anyfish.com', '157111122221', '150341']
In [103]: Contact = namedtuple("Contact", reader.next())
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-103-c7de0ce0abbc> in <module>()
----> 1 Contact = namedtuple("Contact", reader.next())
/usr/local/python27/lib/python2.7/collections.pyc in namedtuple(typename, field_names, verbose, rename)
329 if not all(c.isalnum() or c=='_' for c in name):
330 raise ValueError('Type names and field names can only contain '
--> 331 'alphanumeric characters and underscores: %r' % na
In [105]:
In [105]: reader = csv.reader(open("/tmp/contatcts.csv"))
In [106]: Contact = namedtuple("Contact", reader.next())
In [107]: type(Contact) #是一个类
Out[107]: type
In [108]: c = Contact(*reader.next()) #实例化
In [109]: c.
c.count c.email c.gender c.index c.name c.phone c.sn
In [109]: c.name
Out[109]: 'comyn'
In [110]: c.name
Out[110]: 'comyn'
In [111]: c.name
Out[111]: 'comyn'
In [112]: c = Contact(*reader.next())
In [113]: c.name
Out[113]: 'comyn1'
In [114]: for line in reader:
.....: contacts.append(Contact(*reader.next()))
.....:
In [115]: print contacts
[Contact(name='comyn', gender='man', email='xj@anyfish.com', phone='15711112222', sn='15034'), Contact(name='comyn', gender='man', email='xj@anyfish.com', phone='15711112222', sn='15034'), Contact(name='comyn', gender='man', email='xj@anyfish.com', phone='15711112222', sn='15034'), Contact(name='comyn2', gender='man', email='xj2@anyfish.com', phone='15711112222', sn='150342')] #这里显示的不完成,是因为前面为了显示的详细,指针跑到后面几行了
2、deque
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
In [50]: help(deque)
Help on class deque in module collections:
class deque(__builtin__.object)
| deque([iterable[, maxlen]]) --> deque object #可迭代对象,队列最大长度
In [10]: from collections import deque
In [11]: q = deque(["a", "b", "c", 1])
In [12]: q.
q.append q.clear q.extend q.maxlen q.popleft q.reverse
q.appendleft q.count q.extendleft q.pop q.remove q.rotate
In [12]: q.pop()
Out[12]: 1
In [13]: q.pop()
Out[13]: 'c'
In [14]: q.popleft()
Out[14]: 'a'
In [15]: q.popleft()
Out[15]: 'b'
In [16]: q
Out[16]: deque([])
In [17]: q.ap
q.append q.appendleft
In [17]: q.append("a")
In [18]: q.append("b")
In [19]: q
Out[19]: deque(['a', 'b'])
In [20]: q.appendleft("1")
In [21]: q
Out[21]: deque(['1', 'a', 'b'])
deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。
双端队列(列表),它最大的好处就是实现了从队列头部快速增加和取出对象: .popleft(), .appendleft() 。
你可能会说,原生的list也可以从头部添加和取出对象啊?就像这样:
l.insert(0, v)
l.pop(0)
但是值得注意的是,list对象的这两种用法的时间复杂度是 O(n) ,也就是说随着元素数量的增加耗时呈 线性上升。而使用deque对象则是 O(1)的复杂度,所以当你的代码有这样的需求的时候, 一定要记得使用deque。
作为一个双端队列,deque还提供了一些其他的好用方法,比如 rotate 等。
实际案例:
处理一个日志文件,当文件的某一行,包含你指定的关键字时,你需要返回这一行的前N行
解决方案:
使用队列来实现 #先进先出的数据结构
可以使用list模拟队列
python实现了高效的队列:deque模块
[root@Node3 src]# cat search.py
#!/usr/local/bin/python2.7
#coding = utf-8
import sys
from collections import deque
def search(f, pattern, keep_num):
pre_lines = deque(maxlen=keep_num)
for line in f:
if pattern in line:
yield line, pre_lines
pre_lines.append(line)
if __name__ == '__main__':
log_file = sys.argv[1]
pattern = sys.argv[2]
keep_num = int(sys.argv[3])
with open(log_file) as f:
for line, pre_lines in search(f, pattern, keep_num):
for pline in pre_lines:
print pline
print line
print "-" * 20
[root@Node3 src]# ./search.py /tmp/passwd xj 5
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
xj:uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
--------------------
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
saslauth:x:499:76:"Saslauthd user":/var/empty/saslauth:/sbin/nologin
xj:ostfix:x:89:89::/var/spool/postfix:/sbin/nologin
--------------------
3、 Counter
计数器是我们常用的一个功能,collections中的Counter类就提供了此功能。Counter类是hashtable对象的计数,是dict的子类,从2.7以后的版本加入。
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。
In [6]: type(Counter)
Out[6]: type
In [7]: c = Counter()
In [9]: for i in 'programming':
...: c[i] += 1 #当c的键不存在时值为0,并不会报错
...:
In [10]: c
Out[10]: Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
In [12]: c = Counter('programming')
In [13]: c
Out[13]: Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
In [20]: c.
c.clear c.has_key c.keys c.subtract c.viewvalues
c.copy c.items c.most_common c.update
c.elements c.iteritems c.pop c.values
c.fromkeys c.iterkeys c.popitem c.viewitems
c.get c.itervalues c.setdefault c.viewkeys
In [20]: c.most_common(3) #出线次数最高的前3个元素
Out[20]: [('g', 2), ('m', 2), ('r', 2)]
In [21]: c.most_common(2)
Out[21]: [('g', 2), ('m', 2)]
实际案例:
统计《哈姆雷特》中出现频率最高的前10个单词
[root@Node3 tmp]# cp /etc/fstab hmlt.txt
[root@Node3 tmp]# cat hmlt.txt
#
# /etc/fstab
# Created by anaconda on Fri Aug 5 17:32:21 2016
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=e0c0bc25-62e5-4896-8749-fce761bc3af7 / ext4 defaults 1 1
UUID=970e070d-b49f-439b-8bbb-11ef49ee95ce /boot ext4 defaults 1 2
UUID=7e6bc97d-be6d-4ac9-bc26-8be3d80e715d swap swap defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
sysfs /sys sysfs defaults 0 0
proc /proc proc defaults 0 0
/dev/sda4 /mogstore ext4 defaults 0 0
解决方案:
解析出所有单词
使用Counter模块计数
[root@Node3 src]# cat Counter.py
#!/usr/local/bin/python.27
#coding = utf-8
import sys
import re
from collections import Counter
with open(sys.argv[1]) as f:
worlds = re.findall(r"\w+", f.read())
print Counter(worlds).most_common(10)
[root@Node3 src]# python2.7 Counter.py /tmp/hmlt.txt
[('0', 12), ('defaults', 7), ('dev', 4), ('ext4', 3), ('1', 3), ('5', 3), ('8', 3), ('proc', 3), ('UUID', 3), ('fstab', 2)]
4、OrderedDict
排序字典,是字典的子类。从2.7版本后引入。 在python中,dict,set等数据结构的key是hash无序的。有时候,我们需要得到排序的字典。
注意:OrderedDict的Key会按照插入的顺序排列,不是Key本身排序
实际案例:
有一些task,需要保存到字典中,key为名称,value为内容,但是执行的时候,需要保持存储时的顺序
In [1]: from collections import OrderedDict
In [2]: od = O
OSError OrderedDict Out OverflowError
In [2]: od = OrderedDict()
In [3]: type(od)
Out[3]: collections.OrderedDict
In [4]: od
Out[4]: OrderedDict()
In [5]: od["foo"] = 1
In [6]: od["bar"] = 2
In [7]: od["spam"] =3
In [8]: od["grok"] = 4
In [9]: od
Out[9]: OrderedDict([('foo', 1), ('bar', 2), ('spam', 3), ('grok', 4)])
In [10]: od.
od.clear od.has_key od.itervalues od.setdefault od.viewkeys
od.copy od.items od.keys od.update od.viewvalues
od.fromkeys od.iteritems od.pop od.values
od.get od.iterkeys od.popitem od.viewitems
In [11]: od.keys()
Out[11]: ['foo', 'bar', 'spam', 'grok']
In [12]: od.values()
Out[12]: [1, 2, 3, 4]
In [13]: od.it()
od.items od.iteritems od.iterkeys od.itervalues
In [13]: od.items()
Out[13]: [('foo', 1), ('bar', 2), ('spam', 3), ('grok', 4)]
In [14]: for x, y in od.it
od.items od.iteritems od.iterkeys od.itervalues
In [15]: for x, y in od.items():
print x, y
....:
foo 1
bar 2
spam 3
grok 4
5、defaultdict
defaultdict使用工厂函数创建字典,使用的时候不用考虑缺失的key。从2.5版本后引入。
Python原生的dict结构,如果使用d[key]的方式访问,需要先判断key是否存在。如果key在字典中不存在,会抛出一个KeyError的异常(使用d.get()不存在的key时会返回None而不会报错)。
defaultdict就是为解决这个痛点而生的。只要传入一个默认的工厂方法,如果用d[key]的方式访问字典而key不存在,会调用这个工厂方法使用其结果来作为这个key的默认值。
>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在'abc'
>>> dd['key2'] # key2不存在,返回默认值'N/A'
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。
转载于:https://blog.51cto.com/xiexiaojun/1867800