Notes & software courses · Free to learn
Aph's Blog
On this page

Comprehension เจาะลึก

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

เขียน list/dict/set ในบรรทัดเดียวแบบ Pythonic — อ่านง่าย เร็ว และรู้ว่าเมื่อไรไม่ควรใช้

comprehension คือวิธีสร้าง list/dict/set จากการวนซ้ำในบรรทัดเดียว เป็นสำนวนที่คุณจะเห็นในโค้ด Python จริงแทบทุกไฟล์ บทย่อยนี้จะพาไปตั้งแต่รูปแบบพื้นฐาน จนถึงการใส่เงื่อนไข การซ้อนชั้น และเส้นที่ "ไม่ควรข้าม" เพื่อรักษาความอ่านง่าย

พิมพ์ตามทุกตัวอย่าง

คอร์สนี้ควรรัน Python บนเครื่องจริงแล้ว เปิด terminal พิมพ์ python หรือเปิดไฟล์ .py แล้วลองรันทุกชิ้น พร้อมแก้ค่าดูผลลัพธ์ — การลงมือทำคือสิ่งที่ทำให้เข้าใจจริง

จาก for-loop สู่ list comprehension

สมมติเราอยากได้ list ของเลขยกกำลังสอง วิธีปกติเขียนด้วย for-loop หลายบรรทัด comprehension ย่อให้เหลือบรรทัดเดียวโดยอ่านได้ว่า "เอา x*x สำหรับทุก x ใน range"

python
# แบบ for-loop ปกติ
squares = []
for x in range(5):
    squares.append(x * x)
print(squares)   # [0, 1, 4, 9, 16]

# แบบ list comprehension — ผลเหมือนกันเป๊ะ
squares = [x * x for x in range(5)]
print(squares)   # [0, 1, 4, 9, 16]

โครงสร้างคือ [นิพจน์ for ตัวแปร in ลำดับ] — ฝั่งซ้ายสุดคือ "สิ่งที่จะเก็บ" ตามด้วย for ที่บอกว่าวนอะไร

ใส่เงื่อนไขด้วย if (กรอง)

เติม if ต่อท้ายเพื่อกรองให้เก็บเฉพาะตัวที่ผ่านเงื่อนไข

python
# เก็บเฉพาะเลขคู่
evens = [x for x in range(10) if x % 2 == 0]
print(evens)     # [0, 2, 4, 6, 8]

# เก็บเลขคู่แล้วยกกำลังสอง
even_sq = [x * x for x in range(10) if x % 2 == 0]
print(even_sq)   # [0, 4, 16, 36, 64]

if/else แบบนิพจน์ (วางไว้ข้างหน้า)

ถ้าอยากเลือกค่า (ไม่ใช่กรองทิ้ง) ให้ใช้ if/else เป็นนิพจน์วางไว้ "ก่อน" for สังเกตตำแหน่งให้ดี — กรองใช้ if ท้าย, เลือกค่าใช้ if/else หน้า

python
# แปลงเป็นข้อความ คู่/คี่
labels = ["คู่" if x % 2 == 0 else "คี่" for x in range(5)]
print(labels)    # ['คู่', 'คี่', 'คู่', 'คี่', 'คู่']
ต้องการตำแหน่ง ifตัวอย่าง
กรองทิ้งบางตัวท้ายสุด[x for x in xs if x > 0]
เลือกค่า (เก็บทุกตัว)หน้า for[x if x > 0 else 0 for x in xs]

dict & set comprehension

หลักการเดียวกัน แค่เปลี่ยนวงเล็บ — ใช้ { } และสำหรับ dict ใส่ key: value

python
# dict comprehension: สร้าง map ของเลข -> กำลังสอง
square_map = {x: x * x for x in range(5)}
print(square_map)   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# set comprehension: เก็บความยาวคำที่ไม่ซ้ำ
words = ["hi", "hello", "hey", "yo"]
lengths = {len(w) for w in words}
print(lengths)      # {2, 5, 3}  (set ไม่เก็บค่าซ้ำ)

nested comprehension (ซ้อนชั้น)

ใช้แผ่ (flatten) list 2 มิติให้เป็นมิติเดียวได้ แต่ลำดับ for เขียนจากนอกเข้าใน เหมือน for-loop ซ้อนกันตามปกติ

python
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# flatten: อ่านว่า 'เอา num สำหรับทุก row แล้วทุก num ใน row'
flat = [num for row in matrix for num in row]
print(flat)   # [1, 2, 3, 4, 5, 6, 7, 8, 9]
เส้นที่ไม่ควรข้าม

ถ้า comprehension ซ้อนเกิน 2 ชั้น หรือมีเงื่อนไขซับซ้อนจนอ่านทีเดียวไม่เข้าใจ ให้กลับไปใช้ for-loop ปกติ — เป้าหมายคือโค้ดอ่านง่าย ไม่ใช่สั้นที่สุด การยัดทุกอย่างในบรรทัดเดียวคือ code smell

สรุปหัวข้อนี้

  • โครงสร้าง: [นิพจน์ for ตัวแปร in ลำดับ (if เงื่อนไข)]
  • กรองทิ้ง → if ท้ายสุด, เลือกค่า → if/else หน้า for
  • เปลี่ยนวงเล็บได้เป็น dict {k: v ...} และ set {x ...}
  • ซ้อนได้ แต่เกิน 2 ชั้นเมื่อไร ให้กลับไปใช้ for-loop
แบบฝึกหัด

1) สร้าง list ของเลขคู่ตั้งแต่ 1-20 ที่ยกกำลังสองแล้ว 2) จาก ["apple","banana","kiwi"] สร้าง dict ที่ key เป็นคำ value เป็นความยาวคำ 3) flatten [[1,2],[3,4],[5,6]] เป็น [1,2,3,4,5,6] 4) จาก list คะแนน สร้าง list ที่ค่า >=50 เป็น "ผ่าน" ที่เหลือเป็น "ตก"