ข้ามไปยังเนื้อหา

โครงสร้างข้อมูล

Python มี container ประเภทใช้งานทั่วไปสี่ชนิดในตัวภาษา ไม่ต้อง import ใด ๆ

ประเภทSyntaxเปลี่ยนแปลงได้มีลำดับซ้ำได้Hashable
list[1, 2, 3]ได้ได้ได้ไม่ได้
tuple(1, 2, 3)ไม่ได้ได้ได้ได้เฉพาะเมื่อทุก element hashable
dict{"a": 1}ได้ได้ (3.7+)key: ไม่ได้ไม่ได้
set{1, 2, 3}ได้ไม่ได้ไม่ได้ไม่ได้
frozensetfrozenset({1, 2})ไม่ได้ไม่ได้ไม่ได้ได้

แต่ละประเภทมีจุดเด่นต่างกัน: list สำหรับลำดับที่แก้ไขได้, tuple สำหรับข้อมูลคงที่, dict สำหรับการค้นหาด้วย key, และ set สำหรับทดสอบสมาชิกภาพและ set algebra frozenset คือน้องของ set ที่เปลี่ยนแปลงไม่ได้ — แลกการเปลี่ยนแปลงกับความสามารถเป็น dict key หรืออยู่ใน set อื่นได้

Mutable กับ hashable คือขั้วตรงข้ามกัน ทุกประเภทที่เปลี่ยนแปลงได้ในที่นี้ (list, dict, set) ไม่ hashable — เพราะค่าของมันอาจเปลี่ยนหลังจากถูกใช้เป็น key ไปแล้ว ซึ่งจะทำให้สิ่งที่ hash มันไว้เสียหาย tuple hashable แบบมีเงื่อนไข: hashable ก็ต่อเมื่อทุก element ข้างในเป็น hashable ด้วย เพราะ tuple ที่มี element เปลี่ยนแปลงได้ (เช่น list) จะสืบทอดความไม่แน่นอนนั้นมา

  • ใช้ list เมื่อต้องการ append, sort หรือเข้าถึงข้อมูลด้วย index
  • ใช้ tuple เมื่อข้อมูลคงที่และตำแหน่งมีความหมาย (เช่น สี RGB, แถวในฐานข้อมูล)
  • ใช้ dict เมื่อต้องการค้นหาด้วย key อย่างรวดเร็ว หรือจัดกลุ่ม attribute ที่มีชื่อโดยไม่ต้องสร้าง class
  • ใช้ set เมื่อต้องการความเป็นเอกลักษณ์ หรือต้องการ union / intersection / difference
# --- list ---
nums = [1, 2, 3, 4, 5]
print('list:', nums)
print('sum:', sum(nums))
# --- tuple unpacking ---
coords = (10, 20, 30)
x, y, z = coords
print('tuple unpack:', x, y, z)
# --- dict (insertion order preserved since Python 3.7) ---
scores = {'alice': 95, 'bob': 87, 'carol': 92}
print('dict:', scores)
print('alice score:', scores['alice'])
# --- set (duplicates removed automatically) ---
unique = {3, 1, 4, 1, 5, 9, 2, 6, 5}
print('set:', sorted(unique))
# --- hashability decides what can be a dict key or set member ---
by_coords = {coords: 'a point'} # tuple works: it's hashable
print('tuple as dict key:', by_coords)
try:
by_list = {nums: 'broken'} # list does not: it's mutable, unhashable
except TypeError as e:
print('TypeError:', e)
list [ 1 | 2 | 3 ] mutable, ordered -- reorder/replace freely
tuple ( 1 | 2 | 3 ) immutable, ordered -- fixed once built
dict { k: v, ... } mutable, ordered keys -- lookup by hash(key)
set { 1, 2, 3 } mutable, unordered -- membership by hash(value)
frozenset immutable set -- hashable, unlike set
hashable = can compute a stable hash() -> usable as a dict key / set member
mutable = value can change after creation without changing identity
these two are opposites: every built-in mutable type here is unhashable
สิ่งที่ได้ประโยชน์ต้นทุน
listมีลำดับ เปลี่ยนแปลงได้ index ได้ตามตำแหน่งmembership test (in) เป็น O(n); insert/remove ที่ไหนก็ได้ยกเว้นท้ายเป็น O(n)
tupleเปลี่ยนแปลงไม่ได้ เบา และ hashable ถ้า element ของมัน hashableโต หด หรือเรียงลำดับใหม่หลังสร้างไม่ได้
dictlookup ด้วย key เฉลี่ย O(1) รักษาลำดับการแทรกทุก key ต้อง hashable
set / frozensetmembership test เฉลี่ย O(1) ลบค่าซ้ำให้ฟรีไม่มีลำดับ — index ไม่ได้ ไม่รับประกันลำดับวนซ้ำ; set เองใช้เป็น dict key ไม่ได้ (มีแค่ frozenset ที่ทำได้)
  • “{} สร้าง set ว่างเปล่า” — ความจริง: {} สร้าง dict ว่างเปล่า วิธีเดียวที่จะสร้าง set ว่างเปล่าคือ set()
  • “collection ทั้งสี่รองรับ operation เดียวกันหมด” — ความจริง: มีแค่ list และ dict เท่านั้นที่รองรับการ assign เข้าตำแหน่งที่มีอยู่แล้ว (lst[0] = x, d[k] = v); tuple, set, และ frozenset ไม่รองรับการ assign เข้า item เลย
  • “เลือก collection type เป็นแค่เรื่อง style” — ความจริง: มันเปลี่ยน complexity class ของ operation ของคุณ (O(1) เทียบ O(n) lookup) และการรับประกันเรื่อง mutability ที่ผู้เรียกใช้พึ่งพาได้ — นี่คือการตัดสินใจเรื่อง correctness และ performance ไม่ใช่เรื่อง style
  • “tuple hashable เสมอเพราะ tuple เปลี่ยนแปลงไม่ได้” — ความจริง: hashable ก็ต่อเมื่อทุก element ข้างในก็ hashable ด้วย tuple ที่มี list อยู่ข้างในไม่ hashable
  • โค้ดเขียน seen = {} แล้วเรียก .add(...) บนมันหรือเปล่า? {} คือ dict ไม่ใช่ set — .add ไม่มีอยู่บน dict และจะ raise AttributeError ตั้งแต่รันครั้งแรก
  • loop ทำการเช็ค x in some_collection ซ้ำ ๆ กับสิ่งที่เริ่มต้นเป็น list และโตขึ้นเรื่อย ๆ หรือเปล่า? นั่นคือการเช็คแบบ O(n) ทุกรอบ set ทำให้การเช็คเดียวกันเป็น O(1) โดยเฉลี่ย
  • tuple ถูกใช้ราวกับว่ารองรับ .append() หรือการ assign เข้า item “แค่ครั้งนี้ครั้งเดียว” หรือเปล่า? ทั้งสองแบบ raise ทันที — AttributeError หรือ TypeError
  • โค้ดสันนิษฐานว่าลำดับการวนซ้ำของ set มีความหมายหรือทำซ้ำได้เสมอหรือเปล่า? ไม่จริง — มีแค่ list, tuple, และ dict (3.7+) เท่านั้นที่รับประกันเรื่องลำดับ

🐛 หา bug ในโค้ดนี้

def unique_ids(records):
seen = {}
result = []
for record in records:
if record['id'] not in seen:
seen.add(record['id'])
result.append(record)
return result
print(unique_ids([{'id': 1}, {'id': 2}, {'id': 1}]))
เฉลย

seen = {} สร้าง dict ว่างเปล่า ไม่ใช่ set — {} เป็น dict literal เสมอใน Python; การเขียน set ว่างเปล่าคือ set() record['id'] not in seen ยังทำงานได้ (การเช็ค membership กับ dict จะเช็ค key ของมัน) แต่ seen.add(record['id']) ล้มเหลวทันที เพราะ dict ไม่มี method .add — มีแค่ set เท่านั้นที่มี ตรวจสอบแล้วบน Python 3.14: AttributeError: 'dict' object has no attribute 'add' วิธีแก้คือ seen = set()

Collection ประเภทใดที่ไม่อนุญาตให้มีค่าซ้ำ?
โค้ดนี้ raise อะไร? seen = {} seen.add(1)
ประเภท built-in ใดที่มีลำดับ AND เปลี่ยนแปลงไม่ได้ทั้งคู่?
โค้ดนี้ raise อะไร? data = (1, 2, [3, 4]) print(hash(data))
function หนึ่งทำ x in collection ข้างใน loop ที่รันหลายพันครั้ง โดย collection โตไปถึงหลายพัน item ได้ ตัวเลือกไหนหลีกเลี่ยงการเช็คแบบ O(n) ทุกครั้งที่เรียก?