On this page
collections & itertools
เครื่องมือใน standard library ที่มืออาชีพใช้ทุกวัน — เขียนน้อยลง บั๊กน้อยลง
Python มีเครื่องมือสำเร็จรูปใน 2 โมดูลที่ช่วยให้งานซ้ำ ๆ สั้นลงและถูกต้องขึ้น: collections (โครงสร้างข้อมูลพิเศษ) และ itertools (เครื่องมือวนซ้ำ) การรู้จักมันคือสัญญาณของคนที่เขียน Python มาพอสมควร
Counter — นับความถี่
งานนับว่าอะไรเจอกี่ครั้ง ใช้ Counter แทนการเขียน dict เองทีละขั้น
from collections import Counter
words = ["a", "b", "a", "c", "a", "b"]
count = Counter(words)
print(count) # Counter({'a': 3, 'b': 2, 'c': 1})
print(count["a"]) # 3
print(count.most_common(2)) # [('a', 3), ('b', 2)] top-2
# นับตัวอักษรในข้อความก็ได้
print(Counter("banana")) # Counter({'a': 3, 'n': 2, 'b': 1})defaultdict — dict ที่มีค่า default
ปัญหาคลาสสิก: เพิ่มค่าใน dict ที่ key ยังไม่มี ต้องเช็คก่อนทุกครั้ง defaultdict ตั้งค่าเริ่มต้นให้อัตโนมัติ เหมาะกับการจัดกลุ่ม (grouping)
from collections import defaultdict
# จัดกลุ่มนักเรียนตามเกรด
students = [("Aph", "A"), ("Bee", "B"), ("Cha", "A")]
groups = defaultdict(list) # ค่า default ของ key ใหม่ = list ว่าง
for name, grade in students:
groups[grade].append(name) # ไม่ต้องเช็คว่ามี key ไหม
print(dict(groups)) # {'A': ['Aph', 'Cha'], 'B': ['Bee']}
# นับด้วย defaultdict(int) ก็ได้
counts = defaultdict(int)
for ch in "banana":
counts[ch] += 1 # key ใหม่เริ่มที่ 0 อัตโนมัติnamedtuple & dataclass — ข้อมูลที่มีชื่อฟิลด์
แทนที่จะใช้ tuple ที่ต้องจำว่า index ไหนคืออะไร ใช้ namedtuple ให้แต่ละช่องมีชื่อ (สมัยใหม่นิยม dataclass ซึ่งจะเจอในบท type hints)
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y) # 3 4 (อ่านง่ายกว่า p[0], p[1])
print(p) # Point(x=3, y=4)deque — คิวสองหัวที่เร็ว
list ช้าเมื่อเพิ่ม/ลบหัวแถว (O(n)) deque ทำได้เร็ว (O(1)) ทั้งสองหัว เหมาะทำ queue (จะเจออีกในบท Data Structures)
from collections import deque
q = deque([1, 2, 3])
q.append(4) # ต่อท้าย -> [1, 2, 3, 4]
q.appendleft(0) # ต่อหน้า -> [0, 1, 2, 3, 4]
print(q.popleft()) # 0 (ดึงหน้า เร็ว O(1))
print(q.pop()) # 4 (ดึงท้าย)itertools — เครื่องมือวนซ้ำ
itertools มีฟังก์ชันสร้าง/รวม/จับคู่ลำดับที่ใช้บ่อย ทั้งหมดคืน iterator (ประหยัด memory ตามที่เรียนในหัวข้อ generator)
from itertools import chain, combinations, product, count
# chain: ต่อหลาย iterable เข้าด้วยกัน
print(list(chain([1, 2], [3, 4]))) # [1, 2, 3, 4]
# combinations: เลือก r ตัวจากชุด (ไม่สนลำดับ)
print(list(combinations(["a", "b", "c"], 2)))
# [('a','b'), ('a','c'), ('b','c')]
# product: ผลคูณคาร์ทีเซียน (ทุกการจับคู่)
print(list(product([1, 2], ["x", "y"])))
# [(1,'x'), (1,'y'), (2,'x'), (2,'y')]
# count: นับไม่รู้จบ (ใช้คู่ zip/break)
for i, ch in zip(count(1), "abc"):
print(i, ch) # 1 a / 2 b / 3 cไม่ต้องท่องทุกฟังก์ชัน แค่จำว่า "นับความถี่ → Counter, จัดกลุ่ม → defaultdict, จับคู่/เลือกชุด → itertools" เมื่อเจอโจทย์แล้วจะนึกออกว่ามีของพร้อมใช้ ไม่ต้องเขียนเอง
สรุปหัวข้อนี้
- Counter นับความถี่ + most_common(); defaultdict ตั้งค่า default ให้ key ใหม่
- namedtuple ให้ tuple มีชื่อฟิลด์ (สมัยใหม่นิยม dataclass)
- deque เพิ่ม/ลบสองหัวเร็ว O(1) เหมาะทำ queue
- itertools: chain (ต่อ), combinations/product (จับคู่), count (นับไม่รู้จบ)
1) ใช้ Counter หา 3 คำที่เจอบ่อยสุดในข้อความ 2) ใช้ defaultdict(list) จัดกลุ่มคำตามตัวอักษรแรก 3) สร้าง namedtuple ชื่อ Student มีฟิลด์ name, score 4) ใช้ combinations หาคู่ที่เป็นไปได้ทั้งหมดของ [1,2,3,4] เลือกทีละ 2