Python collections——容器數據類型

PS:本文Python 3.6.4,更高Python版本有更豐富的功能

本文代碼





簡介

這個模塊提供Python標準數據類型dict , list , set , 和 tuple 的替代選擇:

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

常用的有:





引入包

from collections import *




ChainMap 鏈映射

ChainMap用於組合多個字典

基本用法

d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}
chain_map = ChainMap(d1, d2)
print(chain_map.maps)
# [{'a': 1, 'b': 2}, {'b': 3, 'c': 4}]
print(chain_map['a'])
# 1

有多個值時,取最開始遇到的

print(chain_map['b'])  # 取最開始的值
# 2

改變了對應字典,ChainMap也會變

d2['c'] = 5
print(chain_map['c'])  # 改變了d2,chain_map也會變
# 5

keys() , values() , items()獲取鍵值對

# 獲取鍵值對
print(list(chain_map.keys()))
print(list(chain_map.values()))
print(list(chain_map.items()))  # 使用方法與dict類似
# ['c', 'b', 'a']
# [5, 2, 1]
# [('c', 5), ('b', 2), ('a', 1)]

new_child() 添加新字典

# 添加新字典
d3 = {'b': 4, 'd': 5, 'e': 6}
new_chain_map = chain_map.new_child(d3)  # d3會被置頂
print(new_chain_map)
print(new_chain_map['b'])  # d3爲最開始的值
# ChainMap({'b': 4, 'd': 5, 'e': 6}, {'a': 1, 'b': 2}, {'b': 3, 'c': 4})
# 4



深度寫和刪除

ChainMap只能刪除第一條字典有的鍵,只能更新第一條字典有的鍵

try:
    del new_chain_map['a']
except Exception as e:
    print(e)  # 只能刪除第一條字典有的鍵

new_chain_map['c'] = 6  # 只能更新第一條字典有的鍵

print(new_chain_map)
# "Key not found in the first mapping: 'a'"
# ChainMap({'b': 4, 'd': 5, 'e': 6, 'c': 6}, {'a': 1, 'b': 2}, {'b': 3, 'c': 5})

實現深度寫和刪除

class DeepChainMap(ChainMap):
    '深度寫和刪除的ChainMap'

    def __setitem__(self, key, value):
        for mapping in self.maps:
            if key in mapping:
                mapping[key] = value
                return
        self.maps[0][key] = value

    def __delitem__(self, key):
        for mapping in self.maps:
            if key in mapping:
                del mapping[key]
                return
        raise KeyError(key)

測試

deep_chain_map = DeepChainMap({'b': 4, 'd': 5, 'e': 6}, {'a': 1, 'b': 2}, {'b': 3, 'c': 5})
print(deep_chain_map)

try:
    del deep_chain_map['a']  # 深度刪除
except Exception as e:
    print(e)

deep_chain_map['c'] = 6  # 深度寫

print(deep_chain_map)
# DeepChainMap({'b': 4, 'd': 5, 'e': 6}, {'a': 1, 'b': 2}, {'b': 3, 'c': 5})
# DeepChainMap({'b': 4, 'd': 5, 'e': 6}, {'b': 2}, {'b': 3, 'c': 6})



實例

讓命令行參數 > 環境變量 > 默認值

import os
import argparse
from collections import ChainMap

defaults = {
    'COLOR': 'red',
    'USERNAME': 'Administrator',
    'JAVA_HOME': 'C:\Java\jdk1.8.0_201'
}

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--COLOR')
parser.add_argument('-u', '--USERNAME')
parser.add_argument('-j', '--JAVA_HOME')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v is not None}

chain_map = ChainMap(command_line_args, os.environ, defaults)  # 命令行參數 > 環境變量 > 默認值
print(chain_map['COLOR'])
print(chain_map['USERNAME'])
print(chain_map['JAVA_HOME'])

直接運行

red
XerCis
C:\Program Files\Java\jdk1.8.0_201

帶參數運行:python test.py -j "D:\Program Files\Java\jdk1.8.0_201"

red
XerCis
D:\Program Files\Java\jdk1.8.0_201




Counter 計數器

Counter類似其他語言的bags或multisets

基本用法

Counter是dict的子類

l = '1112233456'
cnt = Counter(l)
print(cnt)
print(cnt['1'])  #'1'出現的次數
# Counter({'1': 3, '2': 2, '3': 2, '4': 1, '5': 1, '6': 1})
# 3

elements() 所有元素

print(list(cnt.elements()))  # 按出現順序返回所有元素,忽略0和負數
# ['1', '1', '1', '2', '2', '3', '3', '4', '5', '6']

most_common() 最常見元素

print(cnt.most_common(4))  # 按常見程度和出現順序排序
# [('1', 3), ('2', 2), ('3', 2), ('4', 1)]



數學操作

c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
print(c + d)  # 相加
print(c - d)  # 相減
print(c & d)  # 交集,同min(c[x], d[x])
print(c | d)  # 並集,同max(c[x], d[x])
# Counter({'a': 4, 'b': 3})
# Counter({'a': 2})
# Counter({'a': 1, 'b': 1})
# Counter({'a': 3, 'b': 2})



實例

統計列表字典中不同字段的數目,如統計性別

from collections import Counter

x = [
    {'name': '張三', 'gender': 'male'},
    {'name': '李四', 'gender': 'male'},
    {'name': '王五', 'gender': 'female'},
]
print(Counter([i['gender'] for i in x]))
# Counter({'male': 2, 'female': 1})




deque 雙端隊列

deque,念“deck”,double-ended queue的簡稱,雙端隊列

可高效在雙端添加(append)和彈出(pop),時間複雜度都是O(1)

基本用法

可用for…in遍歷

d = deque('def')
for x in d:
    print(x)  # 遍歷
# d
# e
# f

append() 右插

appendleft() 左插

d.append('g')  #右插
d.appendleft('c')  #左插
print(d)
# deque(['c', 'd', 'e', 'f', 'g'])

pop() 右彈

popleft() 左彈

print(d.pop())  # 右彈
print(d.pop())  # 右彈
print(d)
# g
# c
# deque(['d', 'e', 'f'])
print(d[0])  # 第一個
print(d[-1])  # 最後一個
print(list(d))  # 全部元素
print(list(reversed(d)))  # 翻轉
# d
# f
# ['d', 'e', 'f']
# ['f', 'e', 'd']

extend() 右擴展

extendleft() 左擴展

d.extend('ghi')  # 右擴展
print(d)
d.extendleft('cba')  # 左擴展
print(d)
# deque(['d', 'e', 'f', 'g', 'h', 'i'])
# deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])

實例

類似Unix的 tail 功能

def tail(filename, n=10):
    '返回一個文件的最後n行'
    with open(filename) as f:
        return deque(f, n)
with open('a.txt', mode='w') as f:
    for i in range(100):
        f.write(str(i)+'\n')
print(tail('a.txt'))
# deque(['90\n', '91\n', '92\n', '93\n', '94\n', '95\n', '96\n', '97\n', '98\n', '99\n'], maxlen=10)




defaultdict 帶默認值字典

使用普通dict時Key不存在會拋出異常,使用defaultdict時Key不存在會返回一個默認值

defaultdict其他用法和dict無異

基本用法

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)  # 默認值爲列表
for k, v in s:
    d[k].append(v)
print(d)
# defaultdict(<class 'list'>, {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]})

推薦閱讀:Python setdefault和defaultdict誰更快?




namedtuple() 命名元組

命名元組賦予每個位置一個含義,提供可讀性和自文檔性。它們可以用於任何普通元組,並添加了通過名字獲取值的能力

基本用法

Point = namedtuple('Point', ['x', 'y'])  # Point是一個命名元組,第一個位置是x,第二個是y
p = Point(11, 22)  # 用位置或關鍵字參數實例化一個點
print(p)
print(p[0], p[1])  # 下標訪問
print(p.x, p.y)  # 名字訪問
# Point(x=11, y=22)
# 11 22
# 11 22

_make() 從已有序列創建一個實例

t = [11, 22]
t = Point._make(t)  # 從已有序列創建一個實例
print(t)
# Point(x=11, y=22)
# 和元組一樣,不能直接修改值
try:
    t[0] = 22
except Exception as e:
    print(e)
try:
    t.x = 22
except Exception as e:
    print(e)
print(t._replace(x=22))  # 返回新的
print(t)  # 原來的不變

_fields 列出字段名

Point = namedtuple('Point', ['x', 'y'])
Color = namedtuple('Color', 'red green blue')
Pixel = namedtuple('Pixel', Point._fields + Color._fields)
print(Pixel(11, 22, 128, 255, 0))
# Pixel(x=11, y=22, red=128, green=255, blue=0)

實例

  1. CSV

employees.csv

name,age,title,department,paygrade
a,22,elementary,development,1
b,25,intermediate,sales,2
c,30,high,personnel,3
import csv
from collections import namedtuple

EmployeeRecord = namedtuple("EmployeeRecord", "name, age, title, department, paygrade")

for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv"))):
    print(emp.name, emp.title)
# name title
# a elementary
# b intermediate
# c high
  1. sqlite3
import os
import sqlite3
from collections import namedtuple

database = "companydata.db"

# 創建SQLite數據庫
if os.path.exists(database):
    os.remove(database)
connection = sqlite3.connect(database)
cursor = connection.cursor()
cursor.execute("CREATE TABLE 'employees' ('name' text, 'age' text, 'title' text, 'department' text, 'paygrade' text)")
cursor.execute("INSERT INTO 'employees' VALUES ('a','22','elementary','development', '1')")
cursor.execute("INSERT INTO 'employees' VALUES ('b','25','intermediate','sales', '2')")
cursor.execute("INSERT INTO 'employees' VALUES ('c','30','high','personnel', '3')")
connection.commit()
connection.close()

# 從數據庫導入
connection = sqlite3.connect(database)
cursor = connection.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
    print(emp.name, emp.title)
# name title
# a elementary
# b intermediate
# c high




OrderedDict 有序字典

OrderedDict會記住插入順序

Python 3.7中,dict 會保持插入順序,因此 OrderedDict 在Python 3.7+中不再重要




參考文獻

  1. collections — Container datatypes
  2. collections - 廖雪峯的官方網站
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章