Try   HackMD

collections

collections.abc 抽象容器

📑 抽象容器整理表

ChainMap 鏈式字典

# 範例:ChainMap(dict_a, dict_b, dict_c)
# 先從 dict_a 找,找不到再從 dict_b 找,找不到再從 dict_c 找。

from collections import ChainMap


def find_from_dicts(dicts: list[dict[str, int]], key: str):
    for dict in dicts:
        if key in dict:
            return dict[key]
    raise KeyError


if __name__ == "__main__":

    # dict_a 和 dict_c 都有 key: "c1"
    dict_a = {"a1": 1, "a2": 2, "c1": 3}
    dict_b = {"b1": 4, "b2": 5, "b3": 6}
    dict_c = {"c1": 7, "c2": 8, "c3": 9}

    dicts = [dict_a, dict_b, dict_c]

    print(find_from_dicts("a2", dicts))  # 2
    print(find_from_dicts("c1", dicts))  # 3 (回傳最先找到的)
    print(find_from_dicts("b2", dicts))  # 5
    print(find_from_dicts("b4", dicts))  # KeyError

    dicts = ChainMap(dict_a, dict_b, dict_c)

    print(dicts["a2"])  # 2
    print(dicts["c1"])  # 3 (回傳最先找到的)
    print(dicts["b2"])  # 5
    print(dicts["b4"])  # KeyError

Counter 計數字典

# 參考:https://steam.oxxostudio.tw/category/python/library/collections.html#a4
# 說明:Counter 繼承 dict。dict 無法使用加法/集合運算子,而 Counter 則可以。

from collections import Counter


if __name__ == "__main__":

    numbers_list1 = ["A", "B", "F", "F", "G", "G", "G", "H", "H", "H", "I", "I"]
    numbers_list2 = ["A", "B", "B", "E", "E", "G", "G", "I", "I", "I"]
    counter1 = Counter(numbers_list1)
    counter2 = Counter(numbers_list2)
    counter3 = Counter("AGTCTACTGTGGAGAACTC")

    print(counter1)
    # Counter({'G': 3, 'H': 3, 'F': 2, 'I': 2, 'A': 1, 'B': 1})

    print(counter2)
    # Counter({'I': 3, 'B': 2, 'E': 2, 'G': 2, 'A': 1})

    print(counter3)
    # Counter({'A': 5, 'G': 5, 'T': 5, 'C': 4})

    # 並集 (兩集合的最大子集 -> 選出數量最少的那個)
    print(counter1 & counter2)
    # Counter({'G': 2, 'I': 2, 'A': 1, 'B': 1})

    # 聯集 (兩集合的最小父集 -> 選出數量最多的那個)
    print(counter1 | counter2)
    # Counter({'G': 3, 'H': 3, 'I': 3, 'B': 2, 'F': 2, 'E': 2, 'A': 1})

    # 數量相加
    print(counter1 + counter2)
    # Counter({'G': 5, 'I': 5, 'B': 3, 'H': 3, 'A': 2, 'F': 2, 'E': 2})

    # 差集
    print(counter1 - counter2)
    # Counter({'H': 3, 'F': 2, 'G': 1})

    # Top n
    print(counter1.most_common(3))
    # [('G', 3), ('H', 3), ('F', 2)]

ChainMap 預設字典

# 說明:defaultdict 繼承 dict,給定預設工廠,如果有缺漏值會 call 這個預設工廠,其產出值作為預設值

from collections import defaultdict


# builtins 預設工廠
print(int())   # 0
print(list())  # []
print(dict())  # {}
print(set())   # set()
print(tuple()) # ()


# 工廠函式
def no_idea():
    return "Huh?"


if __name__ == "__main__":
    periodic_table = defaultdict(int)
    periodic_table["H"] = 1
    print(periodic_table["He"])
    # 0
    print(periodic_table)
    # defaultdict(<class 'int'>, {'H': 1, 'He': 0})

    information = defaultdict(no_idea)
    information["name"] = "Kira Yoshikage"
    information["age"] = "33"
    information["gender"] = "male"
    print(information["occupation"])
    # Huh?
    print(information)
    # defaultdict(<function no_idea at 0x00000184E23BD1C0>, {'name': 'Kira Yoshikage', 'age': '33', 'gender': 'male', 'occupation': 'Huh?'})

deque 雙端佇列

# 說明:可限制長度。
# 若雙端佇列已滿,但仍 push 東西進去,則另一端會 pop 東西出來。

from collections import deque

if __name__ == "__main__":
    seasons = ["Spring", "Summer", "Fall", "Winter"]
    dq = deque(seasons, maxlen=6)

    print(dq)           # deque(["Spring", "Summer", "Fall", "Winter"], maxlen=6)
    print(dq.pop())     # Winter
    print(dq)           # deque(["Spring", "Summer", "Fall"], maxlen=6)
    print(dq.popleft()) # Spring
    print(dq)           # deque(["Summer", "Fall"], maxlen=6)
    dq.append("Winter")
    print(dq)           # deque(["Summer", "Fall", "Winter"], maxlen=6)
    dq.appendleft("Spring")
    print(dq)           # deque(["Spring", "Summer", "Fall", "Winter"], maxlen=6)
    dq.appendleft("a")
    print(dq)           # deque(["a", "Spring", "Summer", "Fall", "Winter"], maxlen=6)
    dq.appendleft("b")
    print(dq)           # deque(["b", "a", "Spring", "Summer", "Fall", "Winter"], maxlen=6)
    dq.appendleft("c")
    print(dq)           # deque(["c", "b", "a", "Spring", "Summer", "Fall"], maxlen=6)

namedtuple 具名元組

# 說明:
# 類別工廠

# 比較:namedtuple vs tuple
# tuple
#   + 使用數字索引取得值
# namedtuple
#   + 使用字串索引取得值 (✅ 可讀性)

# 比較:namedtuple vs dict
# dict
#   + 搜尋速度 O(1)
# namedtuple
#   + 搜尋速度 O(n) (❌ 比 dict 慢)
#   + 所占空間較小 (✅ 因為它是 immutable object)
#   + IDE 支援屬性提示 (✅ 腦袋過載程序猿的勝利)

# 使用方式:
# 類別 = namedtuple("類別名稱", ["屬性名稱1", "屬性名稱2"])
# 實例 = 類別(屬性值1, 屬性值2)

from collections import namedtuple

if __name__ == "__main__":

    " 類別 "
    info = namedtuple("Character", ["name", "gender", "titan"])

    " 創建實例 "
    Grisha_Jaeger = info("Grisha Jaeger", "male", "Attack Titan")
    print(Grisha_Jaeger)
    # Character(name="Grisha Jaeger", gender="male", titan="Attack Titan")
    print(type(Grisha_Jaeger))
    # <class '__main__.Character'>

    " 實例屬性 "
    print(Grisha_Jaeger.name)   # Grisha Jaeger
    print(Grisha_Jaeger.gender) # male
    print(Grisha_Jaeger.titan)  # Attack Titan

    " 關鍵字初始化 "
    Zeke_Jaeger = info("Zeke Jaeger", titan="Beast Titan", gender="male")
    print(Zeke_Jaeger)
    # Character(name="Zeke Jaeger", gender="male", titan="Beast Titan")

    " 使用已存在實例創建另一實例 "
    Eren_Jaeger = Grisha_Jaeger._replace(name="Eren Jaeger") # (艾連繼承了他老爸的進巨)
    print(Eren_Jaeger)
    # Character(name="Eren Jaeger", gender="male", titan="Attack Titan")
    
    " 轉成字典 "
    print(Eren_Jaeger._asdict())
    # {'name': 'Eren Jaeger', 'gender': 'male', 'titan': 'Attack Titan'}

Ordered 有序字典

# 說明:
# 自從 Python 3.7 後,dict 元素順序為加入順序 (之前是無法預測的)。
# 現在 OrderedDict 已不再那麼重要。

from collections import OrderedDict

if __name__ == "__main__":

    information = {
        "name": "Kira Yoshikage",
        "age": 33,
        "sex": "male",
        "occupation": None,
    }

    for key in information:
        print(key)
        # name
        # age
        # sex
        # occupation

    for key in OrderedDict(information):
        print(key)
        # name
        # age
        # sex
        # occupation