python進階(26)collections標准庫


前言

這個模塊實現了特定目標的容器,以提供Python標准內建容器dict ,list ,set , 和tuple 的替代選擇。
這個模塊提供了以下幾個函數

函數 作用
namedtuple() 創建命名元組子類的工廠函數
deque 類似列表(list)的容器,實現了在兩端快速添加(append)和彈出(pop)
ChainMap 類似字典(dict)的容器類,將多個映射集合到一個視圖里面
Counter 字典的子類,提供了可哈希對象的計數功能
OrderedDict 字典的子類,保存了他們被添加的順序
defaultdict 字典的子類,提供了一個工廠函數,為字典查詢提供一個默認值
UserDict 封裝了字典對象,簡化了字典子類化
UserList 封裝了列表對象,簡化了列表子類化
UserString 封裝了字符串對象,簡化了字符串子類化

 

namedtuple

 

namedtuple的由來

因為元組的局限性:不能為元組內部的數據進行命名,所以往往我們並不知道一個元組所要表達的意義,所以引入namedtuple這個工廠函數,來構造一個帶字段名的元組。namedtuple繼承自tuple
命名元組賦予每個位置一個含義,提供可讀性。它們可以用於任何普通元組,並添加了通過名字獲取值的能力,通過索引值也是可以的。
 

namedtuple的格式

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
  • typename: 返回一個新的元組子類,名為typename。這個新的子類用於創建類元組的對象,可以通過字段名來獲取屬性值,同樣也可以通過索引和迭代獲取值。
  • field_names: 像['x', 'y'] 一樣的字符串序列。另外field_names可以是一個純字符串,用空白或逗號分隔開元素名,比如 'x y' 或者 'x, y' 。
  • rename=False: 如果rename為true,無效字段名會自動轉換成_+索引值,比如 ['abc', 'def', 'ghi', 'abc'] 轉換成 ['abc', '_1', 'ghi', '_3'] , 消除關鍵詞def和重復字段名abc。
  • default=None: defaults 可以為 None 或者是一個默認值的 iterable 。default默認值賦值跟我們平常的默認值相反,default默認值是從最右邊開始,比如field_names中提供了3個字段['x', 'y', 'z'],default默認值設置為(1, 2),那么我們必須為x指定1個值,y默認值為1,z默認值為2
  • module=None: 如果 module 值有定義,命名元組的 module 屬性值就被設置。
     

namedtuple聲明以及實例化

我們首先創建一個User類,定義3個字段nameageheight,並給age設置默認值為18,給height設置了默認值180

User = namedtuple('User', ['name', 'age', 'height'], defaults=(18, 180))
print(User.__mro__)

我們查看結果

(<class '__main__.User'>, <class 'tuple'>, <class 'object'>)

可以看到我們聲明的User類是繼承於tuple,接下來我們創建實例

user1 = User(name='jkc')
user2 = User(name='jkc2', age=20, height=198)
print(user1)
print(user2)
print(user1.name)
print(user2.age)

運行結果為

User(name='jkc', age=18, height=180)
User(name='jkc2', age=20, height=198)
jkc
20

 

namedtuple的方法和屬性

命名元組還支持三個額外的方法和兩個屬性。為了防止字段名沖突,方法和屬性以下划線開始。
_make(iterable) 類方法從存在的序列或迭代實例創建一個新實例。

>>> t = ['jkc3', 25, 190]
>>> User._make(t)
User(name='jkc3', age=25, height=190)

 

_asdict() 返回一個新的 dict ,它將字段名稱映射到它們對應的值

>>> user4 = User(name='jkc4', age=28, height=200)
>>> user4._asdict()
{'name': 'jkc4', 'age': 28, 'height': 200}

 

_replace(**kwargs) 返回一個新的命名元組實例,並將指定域替換為新的值

>>> user5 = User(name='jkc5', age=30, height=210)
>>> user5._replace(age=18)
User(name='jkc5', age=30, height=210)

 

_fields 字符串元組列出了字段名。用於提醒和從現有元組創建一個新的命名元組類型

>>> user5._fields        
('name', 'age', 'height')

 

_field_defaults 字典將字段名稱映射到默認值。

>>> User._field_defaults
{'name': 'jkc', 'age': 18, 'height': 180}

 

轉換一個字典到命名元組,使用 ** 兩星操作符

>>> d = {'name': 'jkc6', 'age': 18, 'height': 180}
>>> User(**d)
User(name='jkc6', age=18, height=180)

 

OrderedDict

有序字典就像常規字典一樣,但有一些與排序操作相關的額外功能。由於內置的 dict 類獲得了記住插入順序的能力(在 Python 3.7 中保證了這種新行為),它們變得不那么重要了。

與dict類的區別

  • 常規的 dict 被設計為非常擅長映射操作。 跟蹤插入順序是次要的
  • OrderedDict 擅長重新排序操作。 空間效率、迭代速度和更新操作的性能是次要的。
  • 算法上, OrderedDict 可以比 dict 更好地處理頻繁的重新排序操作。 這使其適用於跟蹤最近的訪問(例如在 LRU cache 中)。
  • 對於 OrderedDict ,相等操作檢查匹配順序。
  • OrderedDict 類的 popitem() 方法有不同的簽名。它接受一個可選參數來指定彈出哪個元素。
  • OrderedDict 類有一個 move_to_end() 方法,可以有效地將元素移動到任一端。
  • Python 3.8之前, dict 缺少 __reversed__() 方法。
     

popitem(last=True)

有序字典的 popitem() 方法移除並返回一個 (key, value) 鍵值對。 如果 last 值為真,則按 LIFO 后進先出的順序返回鍵值對,否則就按 FIFO 先進先出的順序返回鍵值對。

from collections import OrderedDict


d = OrderedDict({'status': 200, 'message': 'success'})
print(f'原始的有序字典: {d}')
print('被刪除的鍵值對是: ', d.popitem(last=True))  # 后進先出
print(f'被刪除后的有序字典: {d}')

# 結果
原始的有序字典: OrderedDict([('status', 200), ('message', 'success')])
被刪除的鍵值對是:  ('message', 'success')
被刪除后的有序字典: OrderedDict([('status', 200)])
from collections import OrderedDict


d = OrderedDict({'status': 200, 'message': 'success'})
print(f'原始的有序字典: {d}')
print('被刪除的鍵值對是: ', d.popitem(last=False))  # 先進先出
print(f'被刪除后的有序字典: {d}')

# 結果
原始的有序字典: OrderedDict([('status', 200), ('message', 'success')])
被刪除的鍵值對是:  ('status', 200)
被刪除后的有序字典: OrderedDict([('message', 'success')])

 

move_to_end(key, last=True)

將現有 key 移動到有序字典的任一端。 如果 last 為真值(默認)則將元素移至末尾;如果 last 為假值則將元素移至開頭。如果 key 不存在則會觸發 KeyError:

d = OrderedDict({'status': 200, 'message': 'success'})
d.move_to_end('status', last=True)
print('移動后的字典: ', d)
d.move_to_end('status', last=False)
print('移動后的字典', d)

# 結果
移動后的字典:  OrderedDict([('message', 'success'), ('status', 200)])
移動后的字典:  OrderedDict([('status', 200), ('message', 'success')])

 

支持reversed

相對於通常的映射方法,有序字典還另外提供了逆序迭代的支持,通過 reversed()

d = OrderedDict({'status': 200, 'message': 'success'})
print({key: value for key, value in reversed(d.items())})

# 結果
{'message': 'success', 'status': 200}

 

相等測試敏感

OrderedDict 之間的相等測試是順序敏感的

d1 = OrderedDict({'status': 200, 'message': 'success'})
d2 = OrderedDict({'message': 'success', 'status': 200})
d3 = {'status': 200, 'message': 'success'}
d4 = {'message': 'success', 'status': 200}
print('OrderedDict之間的比較結果: ', d1 == d2)
print('dict之間的比較結果: ', d3 == d4)
print('OrderedDict與dict的比較結果: ', d1 == d3 == d4)

# 結果
OrderedDict之間的比較結果:  False
dict之間的比較結果:  True
OrderedDict與dict的比較結果:  True

 

defaultdict

返回一個新的類似字典的對象。 defaultdict 是內置 dict 類的子類。它重載了一個方法並添加了一個可寫的實例變量。其余的功能與 dict 類相同

defaultdict的作用
我們看名字就知道defaultdict的作用是為字典提供一個默認的值,我們正常情況下訪問一個字典的key,如果字典中沒有這個key會報錯

>>> dict1 = {}
>>> dict1['name']
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    dict1['name']
KeyError: 'name'
>>> 

此時我們就可以使用defaultdict,它包含一個名為 default_factory 的屬性,構造時,第一個參數用於為該屬性提供初始值,默認為 None
 

這個default_factory可以是listsetstr,也可以是自定義的函數,作用是當key不存在時,返回的是工廠函數的默認值,比如list對應[ ],str對應的是空字符串,set對應set( ),int對應0

dict1 = defaultdict(int)
dict2 = defaultdict(set)
dict3 = defaultdict(str)
dict4 = defaultdict(list)
print(dict1['name'])
print(dict2['name'])
print(dict3['name'])
print(dict4['name'])

輸出

0
set()

[]

 

小例子1

使用 list 作為 default_factory,我們可以很輕松地將(鍵-值對組成的)序列轉換為(鍵-列表組成的)字典:

>>> from collections import defaultdict
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
	d[k].append(v)

>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

 

小例子2

設置 default_factoryint,使 defaultdict 用於計數

>>> s = 'aiibiicii'
>>> d = defaultdict(int)
>>> for k in s:
	d[k] += 1

>>> sorted(d.items())
[('a', 1), ('b', 1), ('c', 1), ('i', 6)]

 

小例子3

如果你需要自己定義一個返回值,你可以創建1個函數,設置自定義的返回值

def constant_factory(value):
    return lambda: value


d = defaultdict(constant_factory('success'))
d.update(status=200)
var = d['message']
print(sorted(d.items()))

# 輸出
[('message', 'success'), ('status', 200)]

 

Counter對象

它一個計數器工具提供快速和方便的計數。
它是 dict 的子類,用於計數可哈希對象。它是一個集合,元素像字典鍵(key)一樣存儲,它們的計數存儲為值。計數可以是任何整數值,包括0和負數。
 

創建方式

元素從一個 iterable 被計數或從其他的 mapping (or counter)初始化:

c = Counter()                              # a new, empty counter
c = Counter('gallahad')                 # a new counter from an iterable
c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
c = Counter(cats=4, dogs=8)         # a new counter from keyword args

如果引用的鍵沒有任何記錄,就返回一個0,而不是彈出一個 KeyError

>>> c = Counter(['eggs', 'ham'])
>>> c['bacon']
0

 

作為 dict 的子類,Counter 繼承了記住插入順序的功能。 Counter 對象進行數學運算時同樣會保持順序。 結果會先按每個元素在運算符左邊的出現時間排序,然后再按其在運算符右邊的出現時間排序。
 

elements()

返回一個迭代器,其中每個元素將重復出現計數值所指定次。 元素會按首次出現的順序返回。 如果一個元素的計數值小於一,elements() 將會忽略它。

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']

 

most_common([n])

返回一個列表,其中包含 n 個最常見的元素及出現次數,按常見程度由高到低排序。 如果 n 被省略或為 Nonemost_common() 將返回計數器中的所有元素。 計數值相等的元素按首次出現的順序排序:

>>> Counter('abracadabra').most_common()
[('a', 5), ('b', 2), ('r', 2), ('c', 1), ('d', 1)]
>>> Counter('abracadabra').most_common(2)
[('a', 5), ('b', 2)]

 

應用場景

Counter對象一般有以下兩種應用場景
1. 統計單詞在列表中的出現次數

>>> count = Counter()
>>> list1 = ['red', 'blue', 'red', 'green', 'blue', 'blue']
>>> for word in list1:
	count[word] += 1

>>> count
Counter({'blue': 3, 'red': 2, 'green': 1})

count[word]因為沒有在Counter對象中,所以默認情況下會給他賦值為0,因此可以統計出單詞出現的次數
 

2. 找出文件中最常見的十個單詞

>>> import re
>>> words = re.findall(r'\w+', open('log.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1180), ('and', 822), ('to', 810), ('of', 799), ('i', 688),
 ('you', 510),  ('a', 508), ('my', 500), ('yes', 406), ('in', 318)]

 

deque([iterable[, maxlen]])

返回一個新的雙向隊列對象,從左到右初始化(用方法 append()) ,從 iterable (迭代對象) 數據創建。如果 iterable 沒有指定,新隊列為空。
 

Deque隊列是由棧或者queue隊列生成的。Deque 支持線程安全,內存高效添加(append)和彈出(pop),從兩端都可以,兩個方向的大概開銷都是 O(1) 復雜度。
 

雖然 list 對象也支持類似操作,不過這里優化了定長操作和 pop(0)insert(0, v) 的開銷。它們引起 O(n) 內存移動的操作,改變底層數據表達的大小和位置。
 

如果 maxlen 沒有指定或者是 Nonedeques 可以增長到任意長度。否則,deque就限定到指定最大長度。一旦限定長度的deque滿了,當新項加入時,同樣數量的項就從另一端彈出。
 

deque的方法

雙向隊列(deque)對象支持很多方法,大部分方法list都有

方法名 作用
append(x) 添加 x 到右端
appendleft(x) 添加 x 到左端
clear() 移除所有元素,使其長度為0
copy() 創建一份淺拷貝
count(x) 計算 deque 中元素等於 x 的個數
extend(iterable) 擴展deque的右側,通過添加iterable參數中的元素
extendleft(iterable) 擴展deque的左側,通過添加iterable參數中的元素。注意,左添加時,在結果中iterable參數中的順序將被反過來添加
index(x[, start[, stop]]) 返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一個匹配項,如果未找到則引發 ValueError
insert(i, x) 在位置 i 插入 x,如果插入會導致一個限長 deque 超出長度 maxlen 的話,就引發一個 IndexError。
pop() 移去並且返回一個元素,deque 最右側的那一個。 如果沒有元素的話,就引發一個 IndexError
popleft() 移去並且返回一個元素,deque 最左側的那一個。 如果沒有元素的話,就引發 IndexError
remove(value) 移除找到的第一個 value。 如果沒有的話就引發 ValueError
reverse() 將deque逆序排列。返回 None 。
rotate(n=1) 向右循環移動 n 步。 如果 n 是負數,就向左循環。如果deque不是空的,向右循環移動一步就等價於 d.appendleft(d.pop()) , 向左循環一步就等價於 d.append(d.popleft())
maxlen Deque的最大尺寸,如果沒有限定的話就是 None

 

deque 用法

① linux下查看最新日志的命令是:tail -n 2 test.log,deque也可以實現同樣的功能

def tail(filename, n=10):
    with open(filename) as f:
        return deque(f, n)

 

② 維護一個近期添加元素的序列,通過從右邊添加和從左邊彈出

def moving_average(iterable, n=3):
    # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable)
    d = deque(itertools.islice(it, n-1))
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM