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)
<ipython-input-3-15d291305198> in <module>()
----> 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被加入的,它提供了一种链接多个字典的方法,因此它们可以被是做一个对象!ChainMap
有maps
属性,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)
<ipython-input-59-7de62ba7f6e6> in <module>()
----> 1 Counter([[1,2],[3,4]])
D:anacondalibcollections\__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:anacondalibcollections\__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)