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

ข้อมูลซ้อนกันใน Python: ลิสต์ ดิกชันนารี และโครงสร้างซับซ้อน

ปานกลาง25 นาที6 แบบฝึกหัด85 XP

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

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

ลองนึกถึงโรงเรียน โรงเรียนมีห้องเรียน แต่ละห้องมีนักเรียน นักเรียนแต่ละคนมีชื่อ อายุ และลิสต์ของคะแนน นั่นคือข้อมูลในข้อมูลในข้อมูลอีกที หรือที่เรียกว่าข้อมูลซ้อนกัน (nested data)

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

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

ลิสต์ซ้อนในลิสต์ทำงานอย่างไร

ลิสต์มีลิสต์อื่นเป็นสมาชิกได้ ทำให้เกิดโครงสร้างสองมิติ เหมือนกริดหรือตาราง ลองนึกถึงสเปรดชีตที่มีแถวและคอลัมน์

ลิสต์ซ้อนกันที่แทนกริด
# A 3x3 grid (like a tic-tac-toe board)
board = [
['X', 'O', 'X'],
['O', 'X', 'O'],
['X', ' ', 'O'],
]
for row in board:
print(row)
ผลลัพธ์
['X', 'O', 'X']
['O', 'X', 'O']
['X', ' ', 'O']

ถ้าต้องการเข้าถึงสมาชิกในลิสต์ซ้อนกัน ให้ใช้วงเล็บเหลี่ยมสองชุด วงเล็บแรกเลือกแถว วงเล็บที่สองเลือกคอลัมน์

เข้าถึงสมาชิกด้วยดัชนีสองชั้น
board = [
['X', 'O', 'X'],
['O', 'X', 'O'],
['X', ' ', 'O'],
]
# board[row][column]
print(board[0][0]) # X (top-left)
print(board[1][1]) # X (center)
print(board[2][1]) # (space) (bottom-middle)
ผลลัพธ์
X
X

คุณแก้ไขสมาชิกได้ด้วยไวยากรณ์วงเล็บสองชุดแบบเดียวกัน นี่คือวิธีวางหมากในเกม XO (tic-tac-toe)

แก้ไขสมาชิกในลิสต์ซ้อนกัน
board = [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' '],
]
# Place some moves
board[0][0] = 'X' # top-left
board[1][1] = 'O' # center
board[0][2] = 'X' # top-right
for row in board:
print(row)
ผลลัพธ์
['X', ' ', 'X']
[' ', 'O', ' ']
[' ', ' ', ' ']

ดิกชันนารีซ้อนกันทำงานอย่างไร

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

ดิกชันนารีซ้อนกันกับการเข้าถึงด้วยคีย์ต่อกัน
school = {
'Room 101': {
'teacher': 'Ms. Smith',
'subject': 'Math',
'students': 28,
},
'Room 102': {
'teacher': 'Mr. Jones',
'subject': 'English',
'students': 25,
},
}
print(school['Room 101']['teacher']) # Ms. Smith
print(school['Room 102']['students']) # 25
ผลลัพธ์
Ms. Smith
25

วงเล็บเหลี่ยมแต่ละชุดพาคุณลึกลงไปหนึ่งชั้น school['Room 101'] ให้ดิกชันนารีด้านใน จากนั้น ['teacher'] ให้ค่าที่อยู่ในดิกชันนารีด้านในนั้น

คุณเพิ่มคีย์ใหม่หรืออัปเดตค่าได้ในทุกชั้น

แก้ไขดิกชันนารีซ้อนกัน
school = {
'Room 101': {
'teacher': 'Ms. Smith',
'subject': 'Math',
},
}
# Update an existing value
school['Room 101']['subject'] = 'Algebra'
# Add a new key to the inner dict
school['Room 101']['students'] = 30
# Add an entirely new room
school['Room 103'] = {
'teacher': 'Ms. Lee',
'subject': 'Science',
'students': 22,
}
for room, info in school.items():
print(f"{room}: {info['teacher']} teaches {info['subject']}")
ผลลัพธ์
Room 101: Ms. Smith teaches Algebra
Room 103: Ms. Lee teaches Science

ลิสต์ของดิกชันนารี: รูปแบบที่พบบ่อยที่สุด

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

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

ลิสต์ของดิกชันนารี: ข้อมูลนักเรียน
students = [
{'name': 'Alice', 'age': 15, 'grade': 92},
{'name': 'Bob', 'age': 16, 'grade': 85},
{'name': 'Charlie', 'age': 15, 'grade': 97},
]
# Access a specific student
print(students[0]['name']) # Alice
print(students[2]['grade']) # 97
# Loop through all students
for student in students:
print(f"{student['name']} is {student['age']} and scored {student['grade']}")
ผลลัพธ์
Alice
97
Alice is 15 and scored 92
Bob is 16 and scored 85
Charlie is 15 and scored 97

รูปแบบจะเป็น list[index][key] เสมอ ดัชนีเลือกว่าจะใช้ดิกชันนารีตัวไหน คีย์เลือกว่าจะใช้ค่าไหนในดิกชันนารีนั้น

วิธีเข้าถึงข้อมูลที่ซ้อนลึกอย่างปลอดภัย

ข้อมูลในโลกจริงอาจซ้อนกันหลายชั้น บริษัทมีแผนก แต่ละแผนกมีทีม แต่ละทีมมีสมาชิก การเข้าถึงข้อมูลที่ซ้อนลึกจึงต้องใช้วงเล็บหลายชุดต่อกัน

ใช้วงเล็บต่อกันเพื่อเข้าถึงข้อมูลชั้นลึก
company = {
'engineering': {
'backend': {
'members': ['Alice', 'Bob'],
'lead': 'Alice',
},
'frontend': {
'members': ['Charlie', 'Diana'],
'lead': 'Charlie',
},
},
}
# Get the backend team lead
print(company['engineering']['backend']['lead']) # Alice
# Get the second frontend team member
print(company['engineering']['frontend']['members'][1]) # Diana
ผลลัพธ์
Alice
Diana

อันตรายของการซ้อนลึกคือ ถ้าคีย์ไหนหายไป ก็จะเกิด KeyError ถ้าข้อมูลอาจไม่ครบ ให้ใช้เมธอด .get() ซึ่งคืนค่า None (หรือค่าเริ่มต้นที่กำหนด) แทนที่จะทำให้โปรแกรมหยุดทำงาน

เข้าถึงข้อมูลซ้อนกันอย่างปลอดภัยด้วย .get()
user = {
'name': 'Alice',
'profile': {
'bio': 'Python developer',
},
}
# Safe access with .get()
print(user.get('profile', {}).get('bio', 'No bio')) # Python developer
print(user.get('profile', {}).get('website', 'No site')) # No site
print(user.get('settings', {}).get('theme', 'default')) # default
ผลลัพธ์
Python developer
No site
default

วิธีวนลูปผ่านโครงสร้างข้อมูลซ้อนกัน

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

วนลูปผ่านดิกชันนารีของลิสต์
# Each subject with its list of grades
classes = {
'Math': [85, 92, 78, 95],
'English': [88, 76, 91],
'Science': [93, 87, 82, 79, 95],
}
for subject, grades in classes.items():
average = sum(grades) / len(grades)
print(f"{subject}: average = {average:.1f}")
ผลลัพธ์
Math: average = 87.5
English: average = 85.0
Science: average = 87.2
วนลูปผ่านกริด 2 มิติด้วย enumerate
# Nested list: a grid of numbers
grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
# Print each number with its position
for row_index, row in enumerate(grid):
for col_index, value in enumerate(row):
print(f"grid[{row_index}][{col_index}] = {value}")
ผลลัพธ์
grid[0][0] = 1
grid[0][1] = 2
grid[0][2] = 3
grid[1][0] = 4
grid[1][1] = 5
grid[1][2] = 6
grid[2][0] = 7
grid[2][1] = 8
grid[2][2] = 9

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

กรองลิสต์ของ dict โดยตรวจลิสต์ที่ซ้อนอยู่ภายใน
products = [
{'name': 'Laptop', 'price': 999, 'tags': ['electronics', 'computers']},
{'name': 'Book', 'price': 15, 'tags': ['education', 'reading']},
{'name': 'Phone', 'price': 699, 'tags': ['electronics', 'mobile']},
]
# Find all electronics
for product in products:
if 'electronics' in product['tags']:
print(f"{product['name']}: ${product['price']}")
ผลลัพธ์
Laptop: $999
Phone: $699

วิธีสร้างโครงสร้างข้อมูลซ้อนกันตั้งแต่ต้น

ข้อมูลซ้อนกันไม่ได้มีคนเตรียมไว้ให้เสมอไป บางครั้งคุณต้องสร้างเอง รูปแบบที่ใช้บ่อยคือเริ่มจากโครงสร้างว่าง แล้วเพิ่มข้อมูลเข้าไปภายในลูป

สร้างดิกชันนารีของลิสต์ด้วยการจัดกลุ่ม
# Build a dictionary that groups words by their first letter
words = ['apple', 'avocado', 'banana', 'blueberry', 'cherry', 'coconut']
by_letter = {}
for word in words:
first = word[0]
if first not in by_letter:
by_letter[first] = []
by_letter[first].append(word)
for letter, group in by_letter.items():
print(f"{letter}: {group}")
ผลลัพธ์
a: ['apple', 'avocado']
b: ['banana', 'blueberry']
c: ['cherry', 'coconut']

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

รวมลิสต์คู่ขนานเป็นลิสต์ของ dict
# Transform parallel lists into a list of dicts
names = ['Alice', 'Bob', 'Charlie']
ages = [15, 16, 15]
grades = [92, 85, 97]
students = []
for i in range(len(names)):
student = {
'name': names[i],
'age': ages[i],
'grade': grades[i],
}
students.append(student)
for s in students:
print(s)
ผลลัพธ์
{'name': 'Alice', 'age': 15, 'grade': 92}
{'name': 'Bob', 'age': 16, 'grade': 85}
{'name': 'Charlie', 'age': 15, 'grade': 97}

แบบฝึกหัด

เข้าถึงสมาชิกในลิสต์ซ้อนกัน
เขียนโค้ด

คุณมีกริดตัวเลขขนาด 3x3 ให้แสดงค่าตรงกลาง (แถว 1 คอลัมน์ 1) แล้วแสดงค่ามุมขวาล่าง (แถว 2 คอลัมน์ 2) แต่ละค่าอยู่คนละบรรทัด

grid = [
[10, 20, 30],
[40, 50, 60],
[70, 80, 90],
]
# Print the center value
# Print the bottom-right value
ทายผลลัพธ์: การเข้าถึงดิกชันนารีซ้อนกัน
ทายผลลัพธ์

อ่านโค้ดอย่างละเอียด โค้ดจะแสดงอะไร พิมพ์ผลลัพธ์ให้ตรงทุกตัวอักษร

data = {
'user': {
'name': 'Alice',
'scores': [80, 90, 100],
},
}
print(data['user']['name'])
print(data['user']['scores'][2])
หานักเรียนที่ได้คะแนนสูงสุด
เขียนโค้ด

วนลูปผ่านลิสต์ของนักเรียน แล้วหาคนที่ได้คะแนนสูงสุด แสดงชื่อและคะแนนในรูปแบบ: Best: Name (Grade)

students = [
{'name': 'Alice', 'grade': 88},
{'name': 'Bob', 'grade': 95},
{'name': 'Charlie', 'grade': 72},
{'name': 'Diana', 'grade': 91},
]
# Find and print the highest scorer
จัดกลุ่มคำตามตัวอักษรแรก
เขียนโค้ด

จัดกลุ่มคำตามตัวอักษรแรกลงในดิกชันนารี จากนั้นแสดงแต่ละกลุ่มคนละบรรทัดในรูปแบบ: letter: [words]

แสดงตัวอักษรเรียงตามลำดับ

words = ['ant', 'bear', 'apple', 'bat', 'cat', 'cow']
# Group by first letter and print
แก้บั๊ก: กับดักของลิสต์ซ้อนกัน
แก้บั๊ก

โค้ดนี้พยายามสร้างกริด 3x3 แล้วตั้งค่าเฉพาะช่องตรงกลางเป็น "X" แต่มีบางอย่างผิดพลาด ลองรันดู แก้บั๊กให้มีเพียงช่องตรงกลางที่เป็น "X" และช่องอื่นทั้งหมดยังเป็น "."

แสดงแต่ละแถวคนละบรรทัด

row = ['.'] * 3
grid = [row] * 3
grid[1][1] = 'X'
for r in grid:
print(r)
คำนวณคะแนนเฉลี่ยของแต่ละห้อง
เขียนโค้ด

คุณมีดิกชันนารีที่แต่ละคีย์เป็นชื่อห้องเรียน และแต่ละค่าเป็นลิสต์ของดิกชันนารีนักเรียน คำนวณคะแนนเฉลี่ยของแต่ละห้อง แล้วแสดงในรูปแบบ: ClassName: average

ปัดค่าเฉลี่ยแต่ละค่าเป็นทศนิยม 1 ตำแหน่ง แสดงห้องเรียนเรียงตามตัวอักษร

classes = {
'Math': [
{'name': 'Alice', 'grade': 90},
{'name': 'Bob', 'grade': 80},
],
'English': [
{'name': 'Charlie', 'grade': 85},
{'name': 'Diana', 'grade': 95},
{'name': 'Eve', 'grade': 78},
],
}
# Calculate and print each class average

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

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