1. 程式人生 > >python 之 Collections模組

python 之 Collections模組

官方文件:https://yiyibooks.cn/xx/python_352/library/collections.html

參考:

  https://blog.csdn.net/songfreeman/article/details/50502194

  https://www.cnblogs.com/Eva-J/articles/7228075.html#_label15

Collections模組

 在內建資料型別(dict、list、set、tuple)的基礎上,collections模組還提供了幾個額外的資料型別:Counter、deque、defaultdict、namedtuple和OrderedDict等。

  1.namedtuple: 生成可以使用名字來訪問元素內容的tuple

  2.deque: 雙端佇列,可以快速的從另外一側追加和推出物件

  3.Counter: 計數器,主要用來計數

  4.OrderedDict: 有序字典

  5.defaultdict: 帶有預設值的字典

1. Counter

Counter 作為字典(dict)的一個子類用來進行hashtable計數,將元素進行數量統計、計數後返回一個字典,鍵值為元素:值為元素個數

s = 'abcbcaccbbad'
l = ['a','b','c','c','a','b','b']
d = {'2': 3, '3': 2, '17': 2}
# Counter 獲取各元素的個數,返回字典
print(Counter(s))   # Counter({'c': 4, 'b': 4, 'a': 3})
print(Counter(l))   # Counter({'b': 3, 'a': 2, 'c': 2})
print(Counter(d))   # Counter({3: 3, 2: 2, 17: 1})

方法:
 most_common

# most_common(int) 按照元素出現的次數進行從高到低的排序,返回前int個元素的字典
m1 = Counter(s)
print(m1)                 # Counter({'c': 4, 'b': 4, 'a': 3, 'd': 1})
print(m1.most_common(3))  # [('c', 4), ('b', 4), ('a', 3)]

 elements

# elements 返回經過計數器Counter後的元素,返回的是一個迭代器
e1 = Counter(s)
print(''.join(sorted(e1.elements())))  # aaabbbbcccc
e2 = Counter(d)
print(sorted(e2.elements()))  # ['17', '17', '2', '2', '2', '3', '3'] 字典返回value個key

 update

# update 和set集合的update一樣,對集合進行並集更新
u1 = Counter(s)
u1.update('123a')
print(u1)  # Counter({'a': 4, 'c': 4, 'b': 4, '1': 1, '3': 1, '2': 1})

  substract

# substract 和update類似,只是update是做加法,substract做減法,從另一個集合中減去本集合的元素,
sub1 = 'which'
sub2 = 'whatw'
subset = Counter(sub1)
print(subset)   # Counter({'h': 2, 'i': 1, 'c': 1, 'w': 1})
subset.subtract(Counter(sub2))
print(subset)   # Counter({'c': 1, 'i': 1, 'h': 1, 'a': -1, 't': -1, 'w': -1}) sub1中的h變為2,sub2中h為1,減完以後為1

 iteritems
  與字典dict的items類似,返回由Counter生成的字典的所有item,只是在Counter中此方法返回的是一個迭代器,而不是列表
 iterkeys
  與字典dict的keys方法類似,返回由Counter生成的字典的所有key,只是在Counter中此方法返回的是一個迭代器,而不是列表
 itervalues
  與字典dict的values方法類似,返回由Counter生成的字典的所有value,只是在Counter中此方法返回的是一個迭代器,而不是列表

2.deque

deque 包含在檔案_collections.py中,屬於高效能的資料結構(High performance data structures)之一.可以從兩端新增和刪除元素,常用的結構是它的簡化版。

deque常用方法:
 deque

str1 = 'abc123cd'
dq = deque(str1)
print(dq)        # deque(['a', 'b', 'c', '1', '2', '3', 'c', 'd'])

 append
  佇列右邊新增元素
 appendleft
  佇列左邊新增元素

dq = deque('abc123')
dq.append('right')
dq.appendleft('left')
print(dq) # deque(['left', 'a', 'b', 'c', '1', '2', '3', 'right'])

  clear
  clear 清空佇列中的所有元素
 count
  count(value)  返回佇列中包含value的個數,結果型別為 integer
 extend
  extend 佇列右邊擴充套件,可以是列表、元組或字典,如果是字典則將字典的key加入到deque
 extendleft
  extendleft  同extend, 在左邊擴充套件

dq = deque('abc123')
dq.extend({1:10,2:20})
dq.extendleft('L')
print(dq) # deque(['L', 'a', 'b', 'c', '1', '2', '3', 1, 2])

  pop
  pop  移除並且返回佇列右邊的元素
 popleft
  popleft 移除並且返回佇列左邊的元素
 remove
  remove(value) 移除佇列第一個出現的元素(從左往右開始的第一次出現的元素value)
 reverse
  reverse  佇列的所有元素進行反轉
 rotate
  rotate(n) 對佇列的數進行移動,若n<0,則往左移動即將左邊的第一個移動到最後,移動n次,n>0 往右移動

dq = deque([1,2,3,4,5])
dq.rotate(-1) # 左移,1往左移動一位到5後面
print(dq)

 3.defaultdict

預設字典,是字典的一個子類,繼承有字典的方法和屬性,預設字典在進行定義初始化的時候可以指定字典值的預設型別

dic = collections.defaultdict(dict)
dic['k1'].update({'k2':'aaa'})
print(dic)

   我們看上面的例子,字典dic在定義的時候就定義好了值為字典型別,雖然現在字典中還沒有鍵值 k1,但仍然可以執行字典的update方法. 這種操作方式在傳統的字典型別中是無法實現的,必須賦值以後才能進行值得更新操作,否則會報錯。
我看看一下傳統的字典型別

b = dict() b['k1'].append('2') # TypeError: 'type' object is not iterable

注意:初始化時,指定的型別一般是方法不是例項!!!

# 正確的
dic1 = collections.defaultdict(dict)

dic2 = collections.defaultdict(list)

dic3 = collections.defaultdict(lambda: 'N/A')

# 錯誤的 dic4 = collections.defaultdict(list())

 另一個注意點:當從字典中獲取一個未定義 或 不存在的key時,value會以預設值給出!不會報錯!!!

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回預設值
'N/A'

4.OrderedDict

OrderDict 叫做有序字典,也是字典型別(dict)的一個子類,是對字典的一個補充。 前面我們說過,字典型別是一個無序的集合,如果要想將一個傳統的字典型別進行排序一般會怎麼做了,我們可能會將字典的鍵值取出來做排序後在根據鍵值來進行有序的輸出,我們看下面的一個例子:

# 定義傳統字典
dic1 = dict()
# 按順序新增字典內容
dic1['a'] = '123'
dic1['b'] = 'jjj'
dic1['c'] = '394'
dic1['d'] = '999'
print(dic1)    # 結果: {'a': '123', 'c': '394', 'b': 'jjj', 'd': '999'}
# 排序
dic1_key_list = []
for k in dic1.keys():
    dic1_key_list.append(k)
dic1_key_list.sort()
for key in dic1_key_list:
    print('dic1字典排序結果 %s:%s' %(key,dic1[key]))

以上為定義傳統字典型別時的一個簡單排序過程。 如果我們定義一個有序字典時,將不用再如此麻煩, 字典順序將按照錄入順序進行排序且不會改變。

# 定義有序字典
dic2 = OrderedDict()
dic2['a'] = '123'
dic2['b'] = 'jjj'
dic2['c'] = 'abc'
dic2['d'] = '999'
for k, v in dic2.iteritems():
    print('有序字典:%s:%s' %(k,v))

其他對比示例:

示例:
import collections
print "Regular dictionary"
d={}
d['a']='A'
d['b']='B'
d['c']='C'
for k,v in d.items():
    print k,v

print "\nOrder dictionary"
d1 = collections.OrderedDict()
d1['a'] = 'A'
d1['b'] = 'B'
d1['c'] = 'C'
d1['1'] = '1'
d1['2'] = '2'
for k,v in d1.items():
    print k,v

輸出:
Regular dictionary
a A
c C
b B

Order dictionary
a A
b B
c C
1 1
2 2
# 可以看到,同樣是儲存了ABC等幾個元素,但是使用OrderedDict會根據放入元素的先後順序進行排序。所以輸出的值是排好序的。
# OrderedDict物件的字典物件,如果其順序不同那麼Python也會把他們當做是兩個不同的物件,請看示例:
print 'Regular dictionary:'
d2={}
d2['a']='A'
d2['b']='B'
d2['c']='C'

d3={}
d3['c']='C'
d3['a']='A'
d3['b']='B'

print d2 == d3

print '\nOrderedDict:'
d4=collections.OrderedDict()
d4['a']='A'
d4['b']='B'
d4['c']='C'

d5=collections.OrderedDict()
d5['c']='C'
d5['a']='A'
d5['b']='B'

print  d1==d2

輸出:
Regular dictionary:
True

OrderedDict:
False

# 有序排序:
dd = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}
#按key排序
kd = collections.OrderedDict(sorted(dd.items(), key=lambda t: t[0]))
print kd
#按照value排序
vd = collections.OrderedDict(sorted(dd.items(),key=lambda t:t[1]))
print vd

#輸出
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

5.namedtuple

標準的tuple型別使用數字索引來訪問元素,

bob = ('Bob', 30, 'male')
print('Representation:', bob)
 
jane = ('Jane', 29, 'female')
print('\nField by index:', jane[0])
 
print('\nFields by index:')
for p in [bob, jane]:
    print('%s is a %d year old %s' % p)

 這種對於標準的元組訪問,我們需要知道元素對應下標索引值,但當元組的元素很多時,我們可能無法知道每個元素的具體索引值,這個時候就是可命名元組登場的時候了。

namedtuple 的建立是由自己的類工廠namedtuple()進行建立,而不是由標準的元組來進行例項化,通過namedtuple()建立類的引數包括類名稱和一個包含元素名稱的字串

from collections import namedtuple
 
#建立一個namedtuple 類,類名稱為Person,並賦給變數P
P = namedtuple('Person', 'name,age,gender')
print('Type of Person:', type(P))  # Type of Person: <class 'type'>
 
#通過Person類例項化一個物件bob
bob = P(name='Bob', age=30, gender='male')
print('\nRepresentation:', bob)  # Representation: Person(name='Bob', age=30, gender='male')
 
#通過Person類例項化一個物件jane
jane = P(name='Jane', age=29, gender='female')
print('\nField by name:', jane.name)  # Field by name: Jane
 
print('\nFields by index:')
for p in [bob, jane]:
    print('%s is a %d year old %s' % p)
# Fields by index:
# Bob is a 30 year old male
# Jane is a 29 year old female

通過上面的例項可以看出,我們通過namedtuple()建立了一個Person的類,並複製給P變數,Person的類成員包括name,age,gender,並且順序已經定了,在例項化zhangsan這個物件的時候,對張三的屬性進行了定義。這樣我們在訪問zhangsan這個元組的時候就可以通過張三的屬性來複制(zhangsan.name、zhangsan.age等)。這樣就算這個元組有1000個元素我們都能通過元素的名稱來訪問而不用考慮元素的下標索引值。

非法的引數值

使用namedtuple()來建立類的時候,傳遞的成員屬性引數名稱不能非法(不能為系統引數名稱),且引數名稱不能重複,否則會報值錯誤

# 引數欄位的名稱非法,包含系統名稱class
try:
    p = namedtuple('Person','age,name,class,gender')
    print(p._fields)
except ValueError as err:
    print(err)
 
# Type names and field names cannot be a keyword: 'class'
 
# 類成員欄位引數名稱重複 age
try:
    p1 = namedtuple('Person','age,gender,name,age')
    print(p1._fields)
except ValueError as err:
    print(err)
# Encountered duplicate field name: 'age'

但是也有時候我們是無法控制的,如果引數的名稱來自外部,比如是通過讀取資料庫中的內容來傳遞的引數,此時我們無法手工的修改引數名稱,那該如何是好呢! 別擔心,只需要增加一個屬性就OK了,它就是 rename

# 引數欄位的名稱非法,包含系統名稱class
try:
    p = namedtuple('Person','age,name,class,gender',rename=True)
    print(p._fields)
except ValueError as err:
    print(err)
 
# ('age', 'name', '_2', 'gender')
 
# 類成員欄位引數名稱重複 age
try:
    p1 = namedtuple('Person','age,gender,name,age',rename=True)
    print(p1._fields)
except ValueError as err:
    print(err)
# ('age', 'gender', 'name', '_3')

 從以上的例項我們看出,當有引數錯誤的時候,系統自動將錯誤的引數通過增加 "下劃線+引數索引" 的方式自動將引數名稱替換了。

關於 namedtuple 的個人總結

# namedtuple 總結
from collections import namedtuple

# 建立有兩種方式:列表 or 有空格的字串
Website = namedtuple('Website', ['name', 'url', 'founder'])
Website = namedtuple('Website', 'name url founder')

# 賦值有兩種方式:
#	1、可迭代物件,元組 or 列表 

w = ('Sohu', 'http://www.google.com/', u'張朝陽')
l = ['Sina', 'http://www.sina.com.cn/', '王志東']
# 以元組形式賦值
w1 = Website._make(w)
 Website(name='Sohu', url='http://www.google.com/', founder='張朝陽')
# 以列表形式賦值
w2 = Website._make(l)
Website(name='Sina', url='http://www.sina.com.cn/', founder='王志東')

# 除了上邊這樣,我們還可以直接賦值,類似於類的例項化
w3 = Website(name='163', url='http://www.163.com/', founder='丁磊')  (input)
Website(name='163', url='http://www.163.com/', founder='丁磊')  (output)

# 關於點 . 操作
# 我們可以通過 . 的方式獲取資料 當然索引的方式也是可以的
eg:
print(w1.name, w1.url, w1.founder)
Sohu http://www.google.com/ 張朝陽

print(w1[0], w1[1], w1[2])  
Sohu http://www.google.com/ 張朝陽
# 那這樣 通過索引都能獲取資料,我們可以嘗試另一種方式
print('%s %s %s'%w1)
Sohu http://www.google.com/ 張朝陽

# 看到這裡,我理解的是:其實這個方法(函式)實際上丟回的介面還是一個元組,只是有對應的列舉獲取方式
# namedtuple子類不僅可以使用item的index訪問item,還可以通過item的name進行訪問。可以將namedtuple理解為c中的struct結構,其首先將各個item命名,
然後對每個item賦予資料。