介紹
collections 模塊是 Python 標準庫的一部分,提供了多種增強的數(shù)據(jù)類型,包括 namedtuple、deque、Counter、OrderedDict、defaultdict 和 ChainMap。
這些數(shù)據(jù)類型比內(nèi)置類型更靈活高效,適用于特定場景。掌握和合理使用這些數(shù)據(jù)結(jié)構,你會發(fā)現(xiàn)日常開發(fā)效率會非常高。
常見數(shù)據(jù)結(jié)構
namedtuple
namedtuple 是 Python collections 模塊中的一個工廠函數(shù),用于創(chuàng)建具有命名字段的不可變序列。它提供了類似于元組的性能和內(nèi)存效率,同時可以通過名稱訪問其元素,從而提高代碼的可讀性和可維護性。
下面通過一段關于操作 地理坐標(Geolocation) 的代碼來詳細說明:如何創(chuàng)建 namedtuple、訪問其元素,以及 namedtuple 的方法和屬性。
# 導入
from collections import namedtuple
# 定義 namedtuple 類型 Geo
Geo = namedtuple('Geo', ['latitude', 'longitude'])
# 創(chuàng)建幾個 Geo 實例
a_geo = Geo(39.9042, 116.4074)
b_geo = Geo(31.2304, 121.4737)
c_geo = Geo(23.1291, 113.2644)
# 打印地理坐標
print(f"a_geo: {a_geo.latitude}, {a_geo.longitude}") # 輸出: a_geo: 39.9042, 116.4074
print(f"b_geo: {b_geo.latitude}, {b_geo.longitude}") # 輸出: b_geo: 31.2304, 121.4737
print(f"c_geo: {c_geo.latitude}, {c_geo.longitude}") # 輸出: c_geo: 23.1291, 113.2644
# 使用 _make() 方法創(chuàng)建實例
data = [22.5431, 114.0579]
d_geo = Geo._make(data)
print(f"d_geo: {d_geo.latitude}, {d_geo.longitude}") # 輸出: d_geo: 22.5431, 114.0579
# 使用 _asdict() 方法將 namedtuple 轉(zhuǎn)換為字典
a_dict = a_geo._asdict()
print(a_dict) # 輸出: OrderedDict([('latitude', 39.9042), ('longitude', 116.4074)])
# 使用 _replace() 方法更新實例
updated_a = a_geo._replace(latitude=39.913818)
print(updated_a) # 輸出: Geo(latitude=39.913818, longitude=116.4074)
# 查看字段名稱
print(Geo._fields) # 輸出: ('latitude', 'longitude')
應用場景
- 數(shù)據(jù)記錄:用于表示數(shù)據(jù)庫查詢結(jié)果或日志記錄。
- 配置項:用于保存配置信息,例如服務器配置或應用程序設置。
- 數(shù)據(jù)傳輸:用于定義消息格式,方便序列化和傳輸數(shù)據(jù)。
deque
deque 是一個雙端隊列,它支持從兩端進行高效的添加和刪除操作。
基本使用
deque 支持從兩端進行快速的添加和刪除操作。
from collections import deque
# 創(chuàng)建一個空的 deque
my_deque = deque()
# 創(chuàng)建包含元素的 deque
my_deque_with_data = deque([1, 2, 3, 4, 5])
# 添加元素到 deque 的右端
my_deque.append(6)
# 添加元素到 deque 的左端
my_deque.appendleft(0)
# 從 deque 的右端刪除元素
right_element = my_deque.pop()
# 從 deque 的左端刪除元素
left_element = my_deque.popleft()
deque 的方法和屬性
-
append(item): 將元素添加到 deque 的右端。 -
appendleft(item): 將元素添加到 deque 的左端。 -
pop(): 從 deque 的右端刪除并返回一個元素。 -
popleft(): 從 deque 的左端刪除并返回一個元素。 -
extend(iterable): 將可迭代對象中的元素添加到 deque 的右端。 -
extendleft(iterable): 將可迭代對象中的元素添加到 deque 的左端。 -
rotate(n): 將 deque 向右循環(huán)移動 n 步(如果 n 是負數(shù),則向左移動)。 -
clear(): 清空 deque 中的所有元素。 -
count(item): 返回 deque 中等于 item 的元素個數(shù)。 -
reverse(): 將 deque 中的元素逆序排列。 -
copy(): 返回 deque 的淺拷貝。
我們來用這些方法實現(xiàn)一個基于LRU策略(最近最少使用)的緩存類來慢慢感受下deque的高級玩法:
from collections import deque
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = deque(maxlen=capacity) # 使用 deque 作為緩存
self.cache_map = {} # 用于存儲鍵值對的映射關系
def get(self, key):
if key in self.cache_map:
# 如果鍵存在于緩存中,則將其移到緩存隊列的右端(最近使用)
self.cache.remove(key)
self.cache.append(key)
return self.cache_map[key]
else:
return -1
def put(self, key, value):
if key in self.cache_map:
# 如果鍵已存在于緩存中,則更新值并將其移到緩存隊列的右端(最近使用)
self.cache.remove(key)
self.cache.append(key)
self.cache_map[key] = value
else:
if len(self.cache) == self.capacity:
# 如果緩存已滿,則移除最左端(最久未使用)的鍵值對
removed_key = self.cache.popleft()
del self.cache_map[removed_key]
# 將新的鍵值對添加到緩存隊列的右端(最近使用)
self.cache.append(key)
self.cache_map[key] = value
def clear(self):
# 清空緩存隊列和映射關系
self.cache.clear()
self.cache_map.clear()
def extend(self, iterable):
# 將可迭代對象中的元素添加到緩存隊列的右端(最近使用)
self.cache.extend(iterable)
def __repr__(self):
return str(self.cache)
# 測試示例
cache = LRUCache(3)
cache.put(1, 'a')
cache.put(2, 'b')
cache.put(3, 'c')
print(cache) # 輸出: deque([1, 2, 3], maxlen=3)
cache.put(4, 'd')
print(cache)
# 輸出: deque([2, 3, 4], maxlen=3)
# 因為元素超過了3個,所以淘汰了元素 1,添加保留了元素 4
cache.clear()
print(cache) # 輸出: deque([], maxlen=3)
cache.extend([4, 5, 6])
print(cache) # 輸出: deque([4, 5, 6], maxlen=3)
應用場景
- 隊列和棧:用于實現(xiàn)隊列(FIFO)和棧(LIFO)等數(shù)據(jù)結(jié)構。
- 緩存:用于實現(xiàn)LRU(Least Recently Used)緩存算法,保留最近訪問的元素,丟棄最舊的元素。
- 任務調(diào)度:用于實現(xiàn)異步任務調(diào)度器,管理任務隊列并支持快速的入隊和出隊操作。
Counter
常用于計數(shù)可哈希對象。
基本使用
from collections import Counter
# 創(chuàng)建一個 Counter 對象
my_counter = Counter([1, 1, 2, 3, 3, 3, 4, 4, 5])
# 獲取元素的計數(shù)
count_of_3 = my_counter[3] # 輸出: 3
# 更新計數(shù)
my_counter[3] += 1
# 添加新元素
my_counter[6] = 1
方法和屬性
-
elements(): 返回一個迭代器,包含 Counter 對象中的所有元素,重復次數(shù)與計數(shù)相等。 -
most_common(n): 返回前 n 個最常見的元素及其計數(shù),以列表形式返回。 -
subtract(iterable): 從 Counter 對象中減去可迭代對象中的元素的計數(shù)。 -
update(iterable): 將可迭代對象中的元素添加到 Counter 對象中。 -
clear(): 清空 Counter 對象中的所有元素。 -
copy(): 返回 Counter 對象的淺拷貝。 -
items(): 返回 Counter 對象的鍵值對。 -
keys(): 返回 Counter 對象的鍵。 -
values(): 返回 Counter 對象的值。 -
most_common(n): 返回前 n 個最常見的元素及其計數(shù)。
你會發(fā)現(xiàn)Counter有部分類似dict字段的方法,這是因為Counter類繼承了dict類
通過實現(xiàn)一個打工人出生地的統(tǒng)計計數(shù)器來感受下Counter類的使用:
from collections import Counter
# 模擬一堆大學生的出生地數(shù)據(jù)
birthplaces = ['Beijing', 'Shanghai', 'Guangzhou', 'Beijing', 'Shanghai']
# 創(chuàng)建 Counter 對象統(tǒng)計出生地
birthplace_counter = Counter(birthplaces)
# 獲取出生地為上海的學生人數(shù)
shanghai_students = birthplace_counter['Shanghai']
# 更新計數(shù),添加更多學生的出生地數(shù)據(jù)
more_birthplaces = ['Beijing', 'Shanghai', 'Chongqing', 'Shanghai', 'Shenzhen']
birthplace_counter.update(more_birthplaces)
# 獲取出生地為北京的學生人數(shù)
beijing_students = birthplace_counter['Beijing']
# 統(tǒng)計有哪些地區(qū)
uniq_birthplaces = list(birthplace_counter.keys())
# 獲取計數(shù)最多的 2 個出生地及其計數(shù)
top_birthplaces = birthplace_counter.most_common(2)
# 從統(tǒng)計中減去一部分出生地數(shù)據(jù)
subtract_birthplaces = ['Beijing', 'Shanghai']
birthplace_counter.subtract(subtract_birthplaces)
# 獲取計數(shù)最少的 1 個出生地及其計數(shù)
bottom_birthplace = birthplace_counter.most_common()[-1]
# 清空計數(shù)器
birthplace_counter.clear()
# 輸出結(jié)果
print("出生地為上海的學生人數(shù):", shanghai_students) # 出生地為上海的學生人數(shù): 2
print("出生地為北京的學生人數(shù):", beijing_students) # 出生地為北京的學生人數(shù): 3
print("有哪些出生地:", uniq_birthplaces)
# 有哪些出生地: ['Beijing', 'Shanghai', 'Guangzhou', 'Chongqing', 'Shenzhen']
print("計數(shù)最多的 2 個出生地:", top_birthplaces)
# 計數(shù)最多的 2 個出生地: [('Shanghai', 4), ('Beijing', 3)]
print("計數(shù)最少的 1 個出生地:", bottom_birthplace) # 計數(shù)最少的 1 個出生地: ('Shenzhen', 1)
print("清空后的計數(shù)器:", birthplace_counter) # 清空后的計數(shù)器: Counter()
應用場景
- 文本處理:用于統(tǒng)計單詞出現(xiàn)次數(shù)或字符出現(xiàn)次數(shù)。
- 數(shù)據(jù)分析:用于統(tǒng)計數(shù)據(jù)集中各個元素的出現(xiàn)頻率。
- 詞頻統(tǒng)計:用于生成詞云、繪制頻率分布圖等。
OrderedDict
OrderedDict 是一個有序字典,它可以記住元素的添加順序。雖然 Python3.7 以后版本的dict也改成了有序字典,但基于可讀性和兼容性,依然使用推薦OrderedDict。
基本使用
from collections import OrderedDict
# 創(chuàng)建一個空的 OrderedDict
empty_ordered_dict = OrderedDict()
# 創(chuàng)建包含鍵值對的 OrderedDict
ordered_dict = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# 添加鍵值對到 OrderedDict
ordered_dict['d'] = 4
# 刪除鍵值對
del ordered_dict['a']
# 獲取鍵對應的值
value_of_b = ordered_dict['b']
OrderedDict 的方法和屬性
-
move_to_end(key, last=True): 將指定鍵移動到有序字典的最后或最開始,默認移到最后。 -
popitem(last=True): 彈出有序字典中的最后一個鍵值對或第一個鍵值對,默認彈出最后一個。 -
clear(): 清空有序字典中的所有鍵值對。 -
copy(): 返回有序字典的淺拷貝。 -
keys(): 返回有序字典中的所有鍵。 -
values(): 返回有序字典中的所有值。 -
items(): 返回有序字典中的所有鍵值對。
OrderedDict和Counter一樣,也是繼承了dict一樣
應用場景
- 配置文件:用于保存配置信息,并保持配置項的順序與文件中的順序一致。
- 歷史記錄:用于記錄用戶操作歷史,并保持操作的順序。
- 命令行參數(shù):用于保存命令行參數(shù),并保持參數(shù)的順序與輸入順序一致。
defaultdict
defaultdict 是一種字典的子類,它允許給每個鍵一個默認值,從而避免了在訪問不存在的鍵時引發(fā) KeyError 異常,這在實際開發(fā)中非常有用
基本使用
from collections import defaultdict
# 創(chuàng)建一個默認字典,指定默認值為 int 類型的 0
default_dict = defaultdict(int)
# 添加鍵值對到 defaultdict
default_dict['a'] = 1
# 獲取鍵對應的值,如果鍵不存在,則返回默認值, int 默認返回0
value_of_b = default_dict['b']
print(value_of_b) # 輸出:0
# 刪除鍵值對
# 當然刪除不存在的鍵值對還是會觸發(fā) KeyError 的
del default_dict['a']
# 創(chuàng)建一個默認字典,指定默認值為 list 類型的空列表
default_dict_list = defaultdict(list)
print(default_dict_list["a"]) # 輸出:[]
defaultdict 的方法和屬性
-
default_factory: 默認工廠函數(shù),用于生成默認值。 -
copy(): 返回 defaultdict 對象的淺拷貝。 -
keys(): 返回 defaultdict 對象中的所有鍵。 -
values(): 返回 defaultdict 對象中的所有值。 -
items(): 返回 defaultdict 對象中的所有鍵值對。
defaultdict也是繼承了dict,所以很多方法都很類似,區(qū)別在于defaultdict允許給每個鍵一個默認值
重點說說defualtdict中的default_factory:
default_factory除了設置為某種類型(如 int、list 等)外,還可以將其設置為函數(shù),以便在需要時動態(tài)生成默認值(任何對象:數(shù)值、字符串、類實例等),從而實現(xiàn)更靈活的功能。
下面則是使用這種方法構造默認采集配置:
from collections import defaultdict
# 定義默認的爬蟲配置參數(shù)
def default_config():
return {
'user_agent': 'Mozilla/5.0 ***',
'timeout': 10,
'retry': 3,
'headers': {'Accept': 'text/html,application/json'},
'proxies': None
}
# 創(chuàng)建一個 defaultdict,并指定默認值的生成函數(shù)為 default_config
crawler_config = defaultdict(default_config)
print(crawler_config["baidu"]["timeout"]) # 輸出:10
print(crawler_config["weixin"]["timeout"]) # 輸出:10
應用場景
- 計數(shù)器初始化:用于創(chuàng)建計數(shù)器字典,并設置默認計數(shù)值為0。
- 數(shù)據(jù)分組:用于分組數(shù)據(jù)集,并將缺失的組初始化為空列表或其他默認值。
- 統(tǒng)計分析:用于統(tǒng)計數(shù)據(jù)集中各類別的頻率,并將缺失的類別初始化為0。
ChainMap
用于將多個字典或映射組合在一起形成單個視圖,適用于需要處理多個映射的情況。
方法與屬性
-
new_child(m=None): 創(chuàng)建一個新的 ChainMap 對象,將參數(shù) m(字典或映射)添加到鏈的開頭。 -
parents: 返回一個包含所有父映射的新 ChainMap 對象。 -
maps: 返回一個包含所有映射的列表。 -
copy(): 返回 ChainMap 對象的淺拷貝。
用法
下面舉個配置合并的例子來說明下用法:
from collections import ChainMap
# 定義三個模塊的配置信息
module1_config = {'timeout': 10, 'retry': 3}
module2_config = {'user_agent': 'Mozilla/5.0', 'proxies': 1}
module3_config = {'headers': {'Accept': 'text/html'}}
# 使用 ChainMap 合并多個模塊的配置
combined_config = ChainMap(module1_config, module2_config, module3_config)
# 獲取所有映射
print("所有映射:")
print(combined_config.maps)
# 獲取父映射(去除最后一個映射)
parent_maps = combined_config.parents
print("\n父映射:")
for parent_map in parent_maps:
print(parent_map)
# 獲取指定鍵的值
print("\n獲取指定鍵的值:")
print("timeout:", combined_config['timeout'])
print("user_agent:", combined_config['user_agent'])
# 添加新的映射
new_module_config = {'timeout': 20, 'retry': 5}
combined_config = combined_config.new_child(new_module_config)
print("\n添加新的映射后的配置信息:")
print(combined_config)
print(combined_config['proxies'])
應用場景
- 配置管理:用于合并多個配置源(如全局配置、用戶配置、默認配置)并提供統(tǒng)一的配置視圖。
- 命名空間:用于將多個命名空間(如全局命名空間、模塊命名空間、局部命名空間)組合在一起,形成單個命名空間。
- 上下文管理器:用于管理多個上下文,并提供統(tǒng)一的上下文視圖,使得上下文的嵌套和覆蓋更加靈活。
使用優(yōu)勢
-
更具表達性:
collections中的數(shù)據(jù)結(jié)構通常比內(nèi)置的數(shù)據(jù)結(jié)構更具表達性,能夠更清晰地表達程序的意圖,代碼也會簡單很多。 -
更高級的功能:
collections中的數(shù)據(jù)結(jié)構提供了一些額外的功能,這些功能在特定的使用場景下非常有用。 -
性能優(yōu)勢:在某些特定的操作中,
collections中的數(shù)據(jù)結(jié)構可能比內(nèi)置數(shù)據(jù)結(jié)構性能更好。 -
可擴展性:如果你的程序需要更多的功能,
collections模塊提供了一些非常有用的數(shù)據(jù)結(jié)構,可以滿足更廣泛的需求。 -
標準化:使用
collections中的數(shù)據(jù)結(jié)構能夠讓你的代碼更加標準化和易于理解。
如果你覺得本文有幫助到你,非常期待得到你的支持和鼓勵;
如果有其他問題或補充,歡迎評論區(qū)留言交流。