Python标准库之Collections---Container datatype

Deques

Deques,即 Double-ended-queues,是支持线程安全,内存高效的列表类对象。Deques是可改变的,支持索引等类似于list的操作,然而,我们不能直接对Deques进行切片操作。

from collections import deque
dq=deque('abcde')
dq[1:2] #不能直接对Deques进行切片操作

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in ()
----> 1 dq[1:2] #不能直接对Deques进行切片操作

TypeError: sequence index must be integer, not 'slice'

与list相比,Deques的最大的优点就是在deque的开始插入比list在开端的插入更快,尽管deque在末尾插入比list在末尾插入要略微慢些。deque是线程安全的,所以可以用pickle来序列化。

li=['a','b']
li.extend('ef');li

['a', 'b', 'e', 'f']

li.extend(['de']);li

['a', 'b', 'e', 'f', 'de']

一种有效的思考Deques的方法就是通过扩充和消耗列表项,Deques可以从两端进行扩充/消耗列表项。

dq=deque('abc')
dq.append('d');dq

deque(['a', 'b', 'c', 'd'])

dq.appendleft('z');dq

deque(['z', 'a', 'b', 'c', 'd'])

dq.extend('eft');dq

deque(['z', 'a', 'b', 'c', 'd', 'e', 'f', 't'])

dq.extendleft('yxw');dq

deque(['w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 't'])

我们可以用pop()popleft()来消耗deque:

dq.pop()

't'

dq

deque(['w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f'])

dq.popleft()

'w'

dq

deque(['x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f'])

我们可以使用rotate(n)方法来把最后/最前n项移到最前/最后(n是正数,则是把最后n项移到最前)

dq.rotate(2);dq

deque(['e', 'f', 'x', 'y', 'z', 'a', 'b', 'c', 'd'])

dq.rotate(-2)
dq

deque(['x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f'])

既然我们不能直接对deque进行切片,那么我们可以通过itertools.slice来间接进行切片操作:

import itertools
list(itertools.islice(dq,3,9))

['a', 'b', 'c', 'd', 'e', 'f']

dq

deque(['x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f'])

另一个非常有意思的特性是,Deques支持maxlen选项,该选项限制了deque的最大长度,这使得它非常适合做一个叫循环缓冲器Circular Buffer)的数据结构:

dq2=deque([],maxlen=3)
for i in range(6):
    dq2.append(i)
    print(dq2)

deque([0], maxlen=3)
deque([0, 1], maxlen=3)
deque([0, 1, 2], maxlen=3)
deque([1, 2, 3], maxlen=3)
deque([2, 3, 4], maxlen=3)
deque([3, 4, 5], maxlen=3)

ChainMaps

collections.ChainMap是在Python3.2被加入的,它提供了一种链接多个字典的方法,因此它们可以被是做一个对象!ChainMapmaps属性,new_child()方法,parents属性。我们可以用maps[i]来获取到第i个字典,用parents属性获取它的所有“父类”,尽管字典本身无序,ChainMaps却是一个有序列表字典。

ChainMap在我们用了很多相互关联的字典时候,是非常有用的。它的消耗是遵从优先等级的,先消耗相同关键字的出现在前面的字典的值。

from collections import ChainMap
defaults={'theme':'Default','language':'eng','showIndex':True,'showFooter':True}
cm=ChainMap(defaults)
cm

ChainMap({'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

cm2=cm.new_child({'theme':'blusky'})
cm2['theme']

'blusky'

cm2['showFooter']

True

cm3=cm.new_child({'johnyang':'Yes'})
cm3

ChainMap({'johnyang': 'Yes'}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

cm2.pop('theme')

'blusky'

cm2['theme']

'Default'

cm2['theme']

'Default'

cm2

ChainMap({}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

cm3

ChainMap({'johnyang': 'Yes'}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

cm4=cm.new_child({'theme':'johnyang'})
cm5=cm4.new_child({'showIndex':False})
cm5 #该处可以清楚的看到ChainMap链接了多个字典!

ChainMap({'showIndex': False}, {'theme': 'johnyang'}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

cm5.maps[1]

{'theme': 'johnyang'}

cm5.maps[-1]

{'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True}

cm5.parents

ChainMap({'theme': 'johnyang'}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

ChainMap的优点在于它不仅仅只是一个字典,而是还保存了之前的值,添加一个子字典,覆盖了之前相同关键字对应的值,但是它并没有将之前的关键字的值移除,这样当我们需要保留更改的记录,就可以很容易的回滚到之前的状态。

cm5

ChainMap({'showIndex': False}, {'theme': 'johnyang'}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

cm5.maps[0]={'change?':'Yes'}
cm5

ChainMap({'change?': 'Yes'}, {'theme': 'johnyang'}, {'theme': 'Default', 'language': 'eng', 'showIndex': True, 'showFooter': True})

Counter

Counter 是字典的子类,该子类下,每个字典关键字是一个可哈希的对象,与之相应的是该对象出现次数的数字,有三种方法来初始化:(1)序列,(2)Key:value键值对 (3) (object=value,...)

from collections import Counter
Counter('anysequence')

Counter({'a': 1, 'n': 2, 'y': 1, 's': 1, 'e': 3, 'q': 1, 'u': 1, 'c': 1})

c1=Counter('anyseequence')
c2=Counter({'a':1,'c':1,'e':3})
c3=Counter(a=1,c=1,e=3)
c1

Counter({'a': 1, 'n': 2, 'y': 1, 's': 1, 'e': 4, 'q': 1, 'u': 1, 'c': 1})

c2

Counter({'a': 1, 'c': 1, 'e': 3})

c3

Counter({'a': 1, 'c': 1, 'e': 3})

Counter([(1,2),(3,4),(5,6),(5,6)])

Counter({(1, 2): 1, (3, 4): 1, (5, 6): 2})

Counter([[1,2],[3,4]]) #列表不可哈希,所以报错

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in ()
----> 1 Counter([[1,2],[3,4]])

D:\anaconda\lib\collections_init_.py in init(args, **kwds)
564 raise TypeError('expected at most 1 arguments, got %d' % len(args))
565 super(Counter, self).init()
--> 566 self.update(
args, **kwds)
567
568 def missing(self, key):

D:\anaconda\lib\collections_init_.py in update(*args, **kwds)
651 super(Counter, self).update(iterable) # fast path when counter is empty
652 else:
--> 653 _count_elements(self, iterable)
654 if kwds:
655 self.update(kwds)

TypeError: unhashable type: 'list'

我们也可以创建一个空counter对象,然后通过update方法来填充它:

ct=Counter()
ct.update('abca');ct

Counter({'a': 2, 'b': 1, 'c': 1})

ct.update({'a':3})
ct

Counter({'a': 5, 'b': 1, 'c': 1})

ct.update('def')
ct

Counter({'a': 5, 'b': 1, 'c': 1, 'd': 1, 'e': 1, 'f': 1})

ct.update('aaaa')
ct

Counter({'a': 9, 'b': 1, 'c': 1, 'd': 1, 'e': 1, 'f': 1})

Counter对象与字典最大的不同在于counter对于缺失项返回0,而字典报错。

ct['a']

9

ct['johnyang']

0

ct

Counter({'a': 9, 'b': 1, 'c': 1, 'd': 1, 'e': 1, 'f': 1})

ct.update({'a':-3,'b':-2,'d':3,'e':2})
ct

Counter({'a': 6, 'b': -1, 'c': 1, 'd': 4, 'e': 3, 'f': 1})

可以用elements()方法来返回一个由Counter对象产生的迭代器,该迭代器不包含count值小于1的项,并且是乱序的。

sorted(ct.elements())

['a', 'a', 'a', 'a', 'a', 'a', 'c', 'd', 'd', 'd', 'd', 'e', 'e', 'e', 'f']

另外两个值得一提的方法是most_common()subtract()

ct.most_common()

[('a', 6), ('d', 4), ('e', 3), ('c', 1), ('f', 1), ('b', -1)]

ct.subtract({'a':2})
ct

Counter({'a': 4, 'b': -1, 'c': 1, 'd': 4, 'e': 3, 'f': 1})

Ordered Dictionaries

有序字典的重要性在于它们能记住插入的顺序,所以当遍历它们时候,它们能以被插入的顺序来返回值。而对于普通字典,它们的键值对的顺序是随意的,当我们测试两个字典是否相等时,仅仅考察的是它们的键值对是否一一对应,而没有考虑它们插入的顺序是否也一样,而有序字典则不然,它们除了考虑键值对是否一一对应,还考虑插入顺序是否一致。

from collections import OrderedDict
od1=OrderedDict()
od1['one']=1
od1['two']=2
od2=OrderedDict()
od2['two']=2
od2['one']=1

od1

OrderedDict([('one', 1), ('two', 2)])

od2

OrderedDict([('two', 2), ('one', 1)])

od1==od2

False

a=dict(ok=1,yes=2)
b=dict(yes=2,ok=1)
a==b

True

当我们用update方法来从list中增加值时候,有序字典将会保持它们在list中的顺序。

kvs=[('three',3),('four',4),('fivee',5),('six',6)]
od1.update(kvs)
for k,v in od1.items():print(k,v)

one 1
two 2
three 3
four 4
fivee 5
six 6

defaultdict

defaultdict也是dict的子类,它为字典的初始化提供了一个便利的方法。对于普通字典,当索取一个不存在的键时候,Python将会抛出一个KeyError错误,而defaultdict不会,它将会用default_factory参数给出个默认初始值。

from collections import defaultdict
dd=defaultdict(int)
words=str.split('red blue green red yellow blue red green green red')
for word in words:dd[word]+=1
dd

defaultdict(int, {'red': 4, 'blue': 2, 'green': 3, 'yellow': 1})

def isprimary(c):
    if (c=='red') or (c=='blue') or (c=='green'):
        return True
    else:
        return False
dd2=defaultdict(bool)
for word in words:dd2[word]=isprimary(word)
dd2

defaultdict(bool, {'red': True, 'blue': True, 'green': True, 'yellow': False})

Named Tuples

from collections import namedtuple
space=namedtuple('space','x y z')
s1=space(x=2,y=4,z=10)
s1.x*s1.y*s1.z # 计算体积

80

space2=namedtuple('space2','x def z',rename=True)
s1=space2(3,4,5)
s1.z

5

s1._1

4

named tuple有它自己的3个方法:_make(),_asdict(),_replace(),这些方法都以下划线开头,来防止与field name冲突(本例子中是'space'),_make()将可迭代对象作为输入参数,把它变成named tuple:

sl=[4,5,6]
sl=space._make(sl)
sl

space(x=4, y=5, z=6)

_asdict()返回一个OrderedDict对象:

sl._asdict()

OrderedDict([('x', 4), ('y', 5), ('z', 6)])

_replace()方法返回一个新的实例,将标明的值替换:

sl._replace(x=7,z=9)

space(x=7, y=5, z=9)


posted @ 2021-04-25 23:04  JohnYang819  阅读(171)  评论(0)    收藏  举报