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

Comprehension ใน Python: list, dict, set และ generator

ปานกลาง30 นาที8 แบบฝึกหัด115 XP

บทเรียนนี้แปลจากต้นฉบับภาษาอังกฤษRead in EnglishLeer en españolЧитать на русскомLer em portuguêsแจ้งข้อผิดพลาดในการแปล

0/8 แบบฝึกหัด

ลองนึกภาพว่าคุณมีลิสต์ของตัวเลข 100 ตัว และต้องการลิสต์ใหม่ที่มีเฉพาะเลขคู่ โดยแต่ละตัวคูณ 2 ถ้าใช้ลูปธรรมดาต้องเขียนสี่บรรทัด แต่ถ้าใช้ comprehension ของ Python (การสร้างคอลเลกชันแบบย่อ) ใช้แค่ บรรทัดเดียว

comprehension เป็นหนึ่งในความสามารถของ Python ที่ผู้คนชื่นชอบมากที่สุด ช่วยให้คุณสร้างลิสต์ ดิกชันนารี และเซตใหม่ได้ด้วยการบอกว่า ต้องการอะไร แทนที่จะเขียนคำสั่งทีละขั้นว่าจะสร้าง อย่างไร

ในบทเรียนนี้ คุณจะได้เรียน comprehension ครบทั้งสี่แบบ ได้แก่ list, dict, set และ generator รวมถึงการกรองข้อมูล การใช้เงื่อนไข และคำถามสำคัญว่าเมื่อไร ไม่ควร ใช้ comprehension

List Comprehension: รูปแบบที่ใช้บ่อยที่สุด

list comprehension (การสร้างลิสต์แบบย่อ) สร้างลิสต์ใหม่โดยนำนิพจน์ไปใช้กับสมาชิกแต่ละตัวในลำดับข้อมูลที่มีอยู่ รูปแบบไวยากรณ์คือ [expression for item in iterable]

ให้คิดว่าเป็นลูป for แบบย่อที่สร้างลิสต์ขึ้นมา แทนที่จะสร้างลิสต์ว่างแล้ว append สมาชิกทีละตัวในลูป คุณอธิบายผลลัพธ์ที่ต้องการได้ในบรรทัดเดียว

ลูปธรรมดา (4 บรรทัด)
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
    squares.append(n ** 2)
print(squares)  # [1, 4, 9, 16, 25]
List comprehension (1 บรรทัด)
numbers = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in numbers]
print(squares)  # [1, 4, 9, 16, 25]
List comprehension พื้นฐาน
# Square each number
numbers = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in numbers]
print(squares)
# Convert names to uppercase
names = ['alice', 'bob', 'charlie']
upper_names = [name.upper() for name in names]
print(upper_names)
# Get the length of each word
words = ['hello', 'world', 'python', 'is', 'fun']
lengths = [len(w) for w in words]
print(lengths)
ผลลัพธ์
[1, 4, 9, 16, 25]
['ALICE', 'BOB', 'CHARLIE']
[5, 5, 6, 2, 3]

การเพิ่มเงื่อนไขใน Comprehension

คุณเพิ่มส่วน if เพื่อกรองสมาชิกได้ เฉพาะสมาชิกที่ผ่านเงื่อนไขเท่านั้นที่จะถูกใส่ลงในลิสต์ใหม่ รูปแบบไวยากรณ์คือ [expression for item in iterable if condition]

กรองข้อมูลด้วย if
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Keep only even numbers
evens = [n for n in numbers if n % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
# Keep only words longer than 3 characters
words = ['hi', 'hello', 'hey', 'world', 'ok', 'python']
long_words = [w for w in words if len(w) > 3]
print(long_words) # ['hello', 'world', 'python']
ผลลัพธ์
[2, 4, 6, 8, 10]
['hello', 'world', 'python']

คุณยังใช้นิพจน์ if-else เพื่อแปลงสมาชิกแต่ละตัวให้ต่างกันตามเงื่อนไขได้ด้วย แต่สังเกตว่าไวยากรณ์เปลี่ยนไป if-else ต้องอยู่ ก่อน for ไม่ใช่หลัง

การใช้ if-else ใน comprehension
numbers = [1, 2, 3, 4, 5, 6]
# Label each number as even or odd
labels = ['even' if n % 2 == 0 else 'odd' for n in numbers]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd', 'even']
# Cap scores at 100
raw_scores = [85, 110, 92, 105, 78]
capped = [min(s, 100) for s in raw_scores]
print(capped) # [85, 100, 92, 100, 78]
ผลลัพธ์
['odd', 'even', 'odd', 'even', 'odd', 'even']
[85, 100, 92, 100, 78]

Dict comprehension: สร้างดิกชันนารีแบบย่อ

dict comprehension ทำงานเหมือน list comprehension แต่สร้างดิกชันนารี (dictionary) แทน ใช้วงเล็บปีกกาและนิพจน์แบบ key: value รูปแบบไวยากรณ์คือ {key: value for item in iterable}

Dict comprehension พื้นฐาน
# Create a dict of numbers and their squares
squares = {n: n ** 2 for n in range(1, 6)}
print(squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Create a dict from two lists using zip()
names = ['Alice', 'Bob', 'Charlie']
scores = [95, 87, 72]
grade_book = {name: score for name, score in zip(names, scores)}
print(grade_book) # {'Alice': 95, 'Bob': 87, 'Charlie': 72}
ผลลัพธ์
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{'Alice': 95, 'Bob': 87, 'Charlie': 72}

คุณกรองด้วย if ได้เหมือนกับ list comprehension วิธีนี้เหมาะมากสำหรับการสร้างดิกชันนารีที่มีเพียงบางส่วนของดิกชันนารีเดิม

Dict comprehension แบบกรองและแปลงข้อมูล
prices = {'apple': 1.50, 'banana': 0.75, 'cherry': 3.00, 'date': 5.00}
# Keep only items under $2
affordable = {fruit: price for fruit, price in prices.items() if price < 2}
print(affordable) # {'apple': 1.5, 'banana': 0.75}
# Swap keys and values
flipped = {price: fruit for fruit, price in prices.items()}
print(flipped) # {1.5: 'apple', 0.75: 'banana', 3.0: 'cherry', 5.0: 'date'}
ผลลัพธ์
{'apple': 1.5, 'banana': 0.75}
{1.5: 'apple', 0.75: 'banana', 3.0: 'cherry', 5.0: 'date'}

Set comprehension: สร้างเซตแบบย่อ

set comprehension สร้างเซต ซึ่งเป็นคอลเลกชันที่ ไม่มีสมาชิกซ้ำ ไวยากรณ์ใช้วงเล็บปีกกาเหมือน dict comprehension แต่ไม่มีเครื่องหมายโคลอน รูปแบบไวยากรณ์คือ {expression for item in iterable}

Set comprehension
# Get unique first letters from a list of names
names = ['Alice', 'Anna', 'Bob', 'Charlie', 'Chloe']
first_letters = {name[0] for name in names}
print(first_letters) # {'A', 'B', 'C'}
# Get unique word lengths
sentence = 'the quick brown fox jumps over the lazy dog'
lengths = {len(word) for word in sentence.split()}
print(sorted(lengths)) # [3, 4, 5]
ผลลัพธ์
{'B', 'C', 'A'}
[3, 4, 5]

ความแตกต่างสำคัญจาก list comprehension คือเซตจะลบสมาชิกที่ซ้ำออกให้โดยอัตโนมัติ แม้ "Alice" และ "Anna" จะขึ้นต้นด้วย "A" ทั้งคู่ แต่เซตก็มี "A" อยู่ตัวเดียว

Generator Expression: Comprehension ที่ประหยัดหน่วยความจำ

generator expression หน้าตาเหมือน list comprehension แต่ใช้ วงเล็บกลม แทนวงเล็บเหลี่ยม ความแตกต่างสำคัญคือ generator expression ไม่ได้สร้างสมาชิกทั้งหมดในครั้งเดียว แต่สร้างทีละตัวเมื่อมีการขอใช้

เรื่องนี้สำคัญเมื่อคุณทำงานกับข้อมูลขนาดใหญ่มาก list comprehension ที่มีหนึ่งล้านสมาชิกจะสร้างลิสต์หนึ่งล้านสมาชิกไว้ในหน่วยความจำ ส่วน generator expression แทบไม่ใช้หน่วยความจำเลย เพราะคำนวณสมาชิกแต่ละตัวเฉพาะตอนที่คุณขอเท่านั้น

List comprehension เทียบกับ generator expression
# List comprehension - builds the entire list in memory
squares_list = [n ** 2 for n in range(5)]
print(squares_list) # [0, 1, 4, 9, 16]
print(type(squares_list)) # <class 'list'>
# Generator expression - generates items on demand
squares_gen = (n ** 2 for n in range(5))
print(squares_gen) # <generator object ...>
print(type(squares_gen)) # <class 'generator'>
# You can loop through a generator
for sq in squares_gen:
print(sq, end=' ')
print()
ผลลัพธ์
[0, 1, 4, 9, 16]
<class 'list'>
<generator object <genexpr> at 0x4fc5898>
<class 'generator'>
0 1 4 9 16

generator expression มักใช้ภายในฟังก์ชันอย่าง sum(), max() และ min() และถ้า generator เป็นอาร์กิวเมนต์ตัวเดียวของฟังก์ชัน คุณไม่ต้องใส่วงเล็บซ้อนอีกชั้น

Generator expression ภายในฟังก์ชัน
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Sum of squares using a generator (no extra parentheses needed)
total = sum(n ** 2 for n in numbers)
print(f'Sum of squares: {total}')
# Check if any number is negative
has_negative = any(n < 0 for n in numbers)
print(f'Has negative: {has_negative}')
# Check if all numbers are positive
all_positive = all(n > 0 for n in numbers)
print(f'All positive: {all_positive}')
ผลลัพธ์
Sum of squares: 385
Has negative: False
All positive: True

Comprehension ซ้อนกัน

คุณใส่ส่วน for หนึ่งไว้ภายในอีกส่วนหนึ่งเพื่อจัดการลูปซ้อนกันได้ ลูปนอกมาก่อน แล้วตามด้วยลูปใน วิธีนี้มีประโยชน์สำหรับการแปลงลิสต์ซ้อนให้เป็นลิสต์เดียว (flatten) หรือการสร้างตาราง

แปลงลิสต์ซ้อนให้เป็นลิสต์เดียว
# Flatten a list of lists
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# This is equivalent to:
flat2 = []
for row in matrix:
for num in row:
flat2.append(num)
print(flat2) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
ผลลัพธ์
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
สร้างคู่พิกัด
# Create coordinate pairs
coords = [(x, y) for x in range(3) for y in range(3)]
print(coords)
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
ผลลัพธ์
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

เมื่อไรที่ไม่ควรใช้ Comprehension

comprehension ทรงพลังมาก แต่ก็ไม่ใช่ทางเลือกที่ถูกต้องเสมอไป ถ้า comprehension อ่านยาก ลูปธรรมดาจะดีกว่า ความชัดเจนของโค้ดสำคัญกว่าความแพรวพราวเสมอ

หลีกเลี่ยง comprehension เมื่อ: ตรรกะซับซ้อนและมีหลายเงื่อนไข ต้องการผลข้างเคียง (side effect) เช่น การแสดงผลภายในลูป หรือ comprehension ยาวเกินประมาณ 80 ตัวอักษร

ซับซ้อนเกินไป: อ่านยาก
result = [x * 2 if x > 0 else -x for x in data
         if x != 0 and x % 3 == 0]
ลูปที่ชัดเจน: เข้าใจง่าย
result = []
for x in data:
    if x != 0 and x % 3 == 0:
        if x > 0:
            result.append(x * 2)
        else:
            result.append(-x)

นอกจากนี้ อย่าใช้ comprehension เพียงเพื่อสร้างผลข้างเคียง ถ้าคุณเรียก print() หรือแก้ไขตัวแปรภายนอก ให้ใช้ลูปธรรมดาแทน comprehension ควรมีไว้เพื่อ สร้างคอลเลกชันใหม่ เสมอ


แบบฝึกหัด

ได้เวลาฝึกแล้ว! แบบฝึกหัดเหล่านี้ครอบคลุม comprehension ทั้งสี่แบบ ตั้งแต่พื้นฐานไปจนถึงรูปแบบที่ท้าทายขึ้น

แบบฝึกหัดที่ 1: คูณตัวเลขทุกตัวด้วย 2
เขียนโค้ด

ใช้ list comprehension สร้างลิสต์ชื่อ doubled ที่มีตัวเลขแต่ละตัวจากลิสต์ที่กำหนดให้คูณด้วย 2

ผลลัพธ์ที่ควรได้:

[2, 4, 6, 8, 10]
numbers = [1, 2, 3, 4, 5]
# Create doubled using a list comprehension
print(doubled)
แบบฝึกหัดที่ 2: กรองเฉพาะเลขคู่
เขียนโค้ด

ใช้ list comprehension ที่มีส่วน if สร้างลิสต์ชื่อ evens ที่มีเฉพาะเลขคู่จากลิสต์ที่กำหนดให้

ผลลัพธ์ที่ควรได้:

[4, 12, 8, 20, 6]
numbers = [3, 4, 7, 12, 8, 15, 20, 6, 9]
# Create evens using a list comprehension with filtering
print(evens)
แบบฝึกหัดที่ 3: ทายผลลัพธ์: if กับ if-else
ทายผลลัพธ์

อ่านโค้ดนี้อย่างละเอียดแล้วทายว่าจะแสดงผลอะไร สังเกตตำแหน่งของ if-else ให้ดี

พิมพ์ผลลัพธ์ให้ตรงทุกตัวอักษร

nums = [1, 2, 3, 4, 5]
result = ['even' if n % 2 == 0 else 'odd' for n in nums]
print(result)
แบบฝึกหัดที่ 4: สร้าง Dict Comprehension
เขียนโค้ด

ใช้ dict comprehension สร้างดิกชันนารีชื่อ name_lengths ที่จับคู่ชื่อแต่ละชื่อกับความยาวของชื่อนั้น

ผลลัพธ์ที่ควรได้:

{'Alice': 5, 'Bob': 3, 'Charlie': 7}
names = ['Alice', 'Bob', 'Charlie']
# Create name_lengths using a dict comprehension
print(name_lengths)
แบบฝึกหัดที่ 5: หาสระที่ไม่ซ้ำด้วย Set Comprehension
เขียนโค้ด

ใช้ set comprehension หาสระภาษาอังกฤษทั้งหมดที่ไม่ซ้ำกันในคำที่กำหนดให้ เก็บผลลัพธ์ไว้ในตัวแปรชื่อ vowels_found แล้วแสดงผลเป็นลิสต์ที่ เรียงลำดับแล้ว

ผลลัพธ์ที่ควรได้:

['a', 'e', 'i', 'o']
word = 'conversation'
# Find unique vowels using a set comprehension
# Print them sorted
แบบฝึกหัดที่ 6: แก้บั๊ก: ไวยากรณ์ Comprehension ผิด
แก้บั๊ก

โค้ดนี้พยายามสร้างลิสต์ที่ตัวเลขมากกว่า 5 ได้ป้ายกำกับ 'high' และตัวอื่นได้ป้ายกำกับ 'low' แต่ไวยากรณ์ผิด โปรแกรมจึงหยุดทำงาน แก้ไขให้ถูกต้อง

ผลลัพธ์ที่ควรได้:

['low', 'high', 'low', 'high', 'low', 'high']
nums = [3, 8, 2, 9, 1, 7]
result = ['high' for n in nums if n > 5 else 'low']
print(result)
แบบฝึกหัดที่ 7: เปลี่ยนลูปให้เป็น Comprehension
ปรับปรุงโค้ด

ปรับโครงสร้างโค้ด (refactor) ลูปต่อไปนี้ให้เป็น list comprehension เพียงตัวเดียว โดยผลลัพธ์ต้องเหมือนเดิมทุกประการ

ผลลัพธ์ที่ควรได้:

[1, 9, 25, 49, 81]
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []
for n in numbers:
if n % 2 != 0:
result.append(n ** 2)
print(result)
แบบฝึกหัดที่ 8: แปลงเมทริกซ์เป็นลิสต์เดียว
เขียนโค้ด

ใช้ list comprehension ที่มี for สองส่วน เพื่อแปลงเมทริกซ์ที่กำหนดให้ (ลิสต์ของลิสต์) เป็นลิสต์เดียวที่ไม่ซ้อนกัน จากนั้นแสดงผลรวมของสมาชิกทั้งหมดโดยใช้ generator expression ภายใน sum()

ผลลัพธ์ที่ควรได้:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
45
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# Flatten using a list comprehension with nested for clauses
# Print the flat list
# Print the sum using a generator expression

สรุป: สิ่งที่คุณได้เรียนเกี่ยวกับ Comprehension ใน Python

สรุปทุกสิ่งที่คุณรู้แล้วตอนนี้:

แบบไวยากรณ์สิ่งที่ได้
List comprehension[expr for x in items]ลิสต์
ลิสต์แบบกรอง[expr for x in items if cond]ลิสต์ที่กรองแล้ว
ลิสต์แบบแปลงค่า[a if cond else b for x in items]ลิสต์ที่แปลงค่าแล้ว
Dict comprehension{k: v for x in items}ดิกชันนารี
Set comprehension{expr for x in items}เซต (ไม่มีตัวซ้ำ)
Generator expression(expr for x in items)generator ที่สร้างค่าเมื่อถูกขอ (lazy)
Comprehension ซ้อนกัน[expr for a in X for b in a]ลิสต์เดียวที่ไม่ซ้อนกัน

comprehension เป็นหนึ่งในความสามารถที่เป็นเอกลักษณ์ของ Python ช่วยให้โค้ดสั้นลง อ่านง่ายขึ้น (เมื่อใช้อย่างเหมาะสม) และมักเร็วกว่าลูปที่ทำงานแบบเดียวกัน

บทต่อไปคืออะไร

ตอนนี้คุณสร้างและแปลงคอลเลกชันได้อย่างคล่องแคล่วแล้ว ก็พร้อมไปเรียน ทูเพิลใน Python ซึ่งเป็นลำดับข้อมูลที่เปลี่ยนแปลงไม่ได้ เหมาะกับข้อมูลที่คงที่ เช่น พิกัด ระเบียนข้อมูล และค่าที่ฟังก์ชันคืนกลับมา

คุณจะได้เรียนว่าทำไมการเปลี่ยนแปลงไม่ได้ (immutability) จึงมีประโยชน์ การรวมและแยกค่าในทูเพิล (packing และ unpacking) ทำงานอย่างไร และเมื่อไรควรเลือกใช้ทูเพิลแทนลิสต์

บทเรียนที่เกี่ยวข้อง

บทเรียนนี้มีประโยชน์หรือไม่