Bài 6: Tuples - Bộ Dữ Liệu Bất Biến
Mục Tiêu Bài Học
Sau khi hoàn thành bài này, bạn sẽ:
- ✅ Hiểu tuple là gì và tính chất immutable
- ✅ Tạo và truy cập tuple
- ✅ Sử dụng tuple packing và unpacking
- ✅ Phân biệt tuple vs list
- ✅ Biết khi nào nên dùng tuple
Tuple Là Gì?
Tuple là một collection có thứ tự, không thể thay đổi (immutable), và cho phép các phần tử trùng lặp.
# Tuple với dấu ngoặc tròn ()fruits = ("apple", "banana", "orange")numbers = (1, 2, 3, 4, 5)mixed = (1, "Hello", 3.14, True) print(type(fruits)) # <class 'tuple'>
Immutable - Không thể thay đổi:
my_tuple = (1, 2, 3) # Không thể thay đổi phần tử# my_tuple[0] = 10 # TypeError: 'tuple' object does not support item assignment # Không thể thêm hoặc xóa# my_tuple.append(4) # AttributeError# my_tuple.remove(1) # AttributeError # Nhưng có thể tạo tuple mớinew_tuple = my_tuple + (4, 5)print(new_tuple) # (1, 2, 3, 4, 5)
Tạo Tuple
Cách 1: Dùng Dấu Ngoặc Tròn ()
# Tuple thông thườngcolors = ("red", "green", "blue")numbers = (1, 2, 3, 4, 5) # Empty tupleempty = ()print(len(empty)) # 0 # Single element tuple (cần dấu phẩy!)single = (5,) # ✅ Tuplenot_tuple = (5) # ❌ Này là int, không phải tuple! print(type(single)) # <class 'tuple'>print(type(not_tuple)) # <class 'int'> # Multi-line tupleperson = ( "Alice", 25, "[email protected]", "USA")
Cách 2: Không Cần Dấu Ngoặc (Tuple Packing)
# Tuple packing - tự động tạo tuplecoordinates = 10, 20, 30print(type(coordinates)) # <class 'tuple'>print(coordinates) # (10, 20, 30) # Thường dùng trong returndef get_user(): return "Alice", 25, "[email protected]" user = get_user()print(user) # ('Alice', 25, '[email protected]')
Cách 3: tuple() Constructor
# Từ listmy_list = [1, 2, 3]my_tuple = tuple(my_list)print(my_tuple) # (1, 2, 3) # Từ stringchars = tuple("Python")print(chars) # ('P', 'y', 't', 'h', 'o', 'n') # Từ rangenumbers = tuple(range(5))print(numbers) # (0, 1, 2, 3, 4)
Truy Cập Phần Tử
Giống như list, tuple hỗ trợ indexing và slicing.
Indexing
fruits = ("apple", "banana", "orange", "mango", "grape")# 0 1 2 3 4 (positive)# -5 -4 -3 -2 -1 (negative) # Positive indexingprint(fruits[0]) # appleprint(fruits[2]) # orange # Negative indexingprint(fruits[-1]) # grapeprint(fruits[-2]) # mango # Nested tuplenested = ((1, 2), (3, 4), (5, 6))print(nested[0]) # (1, 2)print(nested[0][1]) # 2print(nested[2][0]) # 5
Slicing
numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) # [start:stop]print(numbers[2:5]) # (2, 3, 4)print(numbers[:3]) # (0, 1, 2)print(numbers[5:]) # (5, 6, 7, 8, 9) # [start:stop:step]print(numbers[::2]) # (0, 2, 4, 6, 8)print(numbers[::-1]) # (9, 8, 7, 6, 5, 4, 3, 2, 1, 0) # Negative indicesprint(numbers[-3:]) # (7, 8, 9)
Tuple Packing và Unpacking
Tuple Packing
Packing là gộp nhiều giá trị thành một tuple.
# Packingpoint = 10, 20print(point) # (10, 20) # Function return multiple valuesdef get_stats(numbers): return min(numbers), max(numbers), sum(numbers) stats = get_stats([1, 2, 3, 4, 5])print(stats) # (1, 5, 15)
Tuple Unpacking
Unpacking là gán từng phần tử của tuple cho các biến riêng.
# Basic unpackingpoint = (10, 20)x, y = pointprint(f"x={x}, y={y}") # x=10, y=20 # Unpacking trong one linename, age, email = ("Alice", 25, "[email protected]")print(name) # Aliceprint(age) # 25print(email) # [email protected] # Swap variablesa, b = 10, 20a, b = b, a # Swap dùng tuple unpackingprint(a, b) # 20 10
Unpacking với * (Extended Unpacking)
# * để lấy phần còn lạifirst, *rest = (1, 2, 3, 4, 5)print(first) # 1print(rest) # [2, 3, 4, 5] # Lấy first và lastfirst, *middle, last = (1, 2, 3, 4, 5)print(first) # 1print(middle) # [2, 3, 4]print(last) # 5 # Skip values với _first, _, third, *_ = (1, 2, 3, 4, 5)print(first, third) # 1 3 # Unpacking trong looppeople = [ ("Alice", 25), ("Bob", 30), ("Charlie", 35)] for name, age in people: print(f"{name} is {age} years old")
Tuple Operations
Concatenation và Repetition
# Concatenation với +tuple1 = (1, 2, 3)tuple2 = (4, 5, 6)result = tuple1 + tuple2print(result) # (1, 2, 3, 4, 5, 6) # Repetition với *pattern = ("A", "B")repeated = pattern * 3print(repeated) # ('A', 'B', 'A', 'B', 'A', 'B') # ❌ Không thể dùng +=, *= trực tiếp (tạo tuple mới)my_tuple = (1, 2)my_tuple = my_tuple + (3,) # Phải gán lạiprint(my_tuple) # (1, 2, 3)
Membership Test
fruits = ("apple", "banana", "orange") # in operatorprint("apple" in fruits) # Trueprint("grape" in fruits) # Falseprint("grape" not in fruits) # True # Nested tuplematrix = ((1, 2), (3, 4), (5, 6))print((3, 4) in matrix) # True
Comparison
# Lexicographical comparisonprint((1, 2, 3) < (1, 2, 4)) # Trueprint((1, 2, 3) == (1, 2, 3)) # Trueprint(("a", "b") < ("a", "c")) # True # So sánh element đầu tiên trướcprint((2, 0) > (1, 100)) # True (2 > 1)
Tuple Methods
Tuple chỉ có 2 methods vì immutable.
1. count() - Đếm phần tử
numbers = (1, 2, 2, 3, 2, 4, 2, 5) count = numbers.count(2)print(count) # 4 # Đếm trong nested tuplenested = ((1, 2), (3, 4), (1, 2))print(nested.count((1, 2))) # 2
2. index() - Tìm vị trí
fruits = ("apple", "banana", "orange", "banana") # Tìm index đầu tiênpos = fruits.index("banana")print(pos) # 1 # Tìm từ vị trí cụ thểpos2 = fruits.index("banana", 2) # Tìm từ index 2print(pos2) # 3 # Lỗi nếu không tìm thấy# fruits.index("grape") # ValueError! # Cách an toànif "grape" in fruits: pos = fruits.index("grape")else: print("Not found")
Built-in Functions với Tuple
numbers = (5, 2, 8, 1, 9, 3) # len() - Độ dàiprint(len(numbers)) # 6 # min(), max() - Nhỏ nhất, lớn nhấtprint(min(numbers)) # 1print(max(numbers)) # 9 # sum() - Tổngprint(sum(numbers)) # 28 # sorted() - Sắp xếp (trả về list!)sorted_nums = sorted(numbers)print(sorted_nums) # [1, 2, 3, 5, 8, 9]print(type(sorted_nums)) # <class 'list'> # Trả về tuple thì phải convertsorted_tuple = tuple(sorted(numbers))print(sorted_tuple) # (1, 2, 3, 5, 8, 9) # any(), all()print(any(numbers)) # True (có ít nhất 1 truthy)print(all(numbers)) # True (tất cả truthy) bools = (True, False, True)print(any(bools)) # Trueprint(all(bools)) # False
Tuple vs List
| Feature | Tuple | List |
|---|---|---|
| Syntax | () |
[] |
| Mutable | ❌ Immutable | ✅ Mutable |
| Performance | ⚡ Nhanh hơn | 🐌 Chậm hơn |
| Memory | 💾 Ít hơn | 💾 Nhiều hơn |
| Methods | 2 methods | 11+ methods |
| Use Case | Fixed data | Dynamic data |
| Hash | ✅ Hashable | ❌ Not hashable |
import sys # Memory comparisonmy_list = [1, 2, 3, 4, 5]my_tuple = (1, 2, 3, 4, 5) print(f"List size: {sys.getsizeof(my_list)} bytes")print(f"Tuple size: {sys.getsizeof(my_tuple)} bytes")# Tuple nhỏ hơn! # Hashable - tuple có thể làm dictionary key# List không thể!point_dict = { (0, 0): "Origin", (1, 1): "Point A", (2, 2): "Point B"}print(point_dict[(0, 0)]) # Origin # ❌ List không làm được# bad_dict = {[0, 0]: "Origin"} # TypeError!
Khi nào dùng Tuple:
- ✅ Data không thay đổi (coordinates, RGB colors, dates)
- ✅ Function return multiple values
- ✅ Dictionary keys
- ✅ Protect data from modification
- ✅ Performance critical (faster than list)
Khi nào dùng List:
- ✅ Data cần thay đổi (add, remove, modify)
- ✅ Unknown size
- ✅ Cần methods như
append(),remove(),sort()
Named Tuples
Named tuples là tuple với tên field, giống struct.
from collections import namedtuple # Define named tuplePoint = namedtuple('Point', ['x', 'y']) # Tạo instancep1 = Point(10, 20)p2 = Point(x=30, y=40) # Truy cập bằng tên (readable!)print(p1.x, p1.y) # 10 20 # Vẫn truy cập được bằng indexprint(p1[0], p1[1]) # 10 20 # Unpacking vẫn hoạt độngx, y = p1print(x, y) # 10 20 # Immutable như tuple thông thường# p1.x = 100 # AttributeError! # Use case: PersonPerson = namedtuple('Person', ['name', 'age', 'email'])alice = Person("Alice", 25, "[email protected]") print(alice.name) # Aliceprint(alice.age) # 25print(alice.email) # [email protected] # Convert to dictprint(alice._asdict())# {'name': 'Alice', 'age': 25, 'email': '[email protected]'}
Ví Dụ Thực Tế
1. RGB Color System
# RGB colors as tuplesRED = (255, 0, 0)GREEN = (0, 255, 0)BLUE = (0, 0, 255)WHITE = (255, 255, 255)BLACK = (0, 0, 0) def mix_colors(color1, color2): """Mix 2 RGB colors (average)""" r1, g1, b1 = color1 r2, g2, b2 = color2 r = (r1 + r2) // 2 g = (g1 + g2) // 2 b = (b1 + b2) // 2 return (r, g, b) purple = mix_colors(RED, BLUE)print(f"Purple: RGB{purple}") # Purple: RGB(127, 0, 127) yellow = mix_colors(RED, GREEN)print(f"Yellow: RGB{yellow}") # Yellow: RGB(127, 127, 0)
2. Database Records
# Database row as tuple (immutable)records = [ (1, "Alice", "[email protected]", 25), (2, "Bob", "[email protected]", 30), (3, "Charlie", "[email protected]", 35)] # Process recordsprint("User List:")for user_id, name, email, age in records: print(f" [{user_id}] {name:<10} ({age}) - {email}") # Filter by ageyoung_users = [r for r in records if r[3] < 30]print(f"\nUsers under 30: {len(young_users)}") # Sort by agesorted_records = sorted(records, key=lambda x: x[3])print("\nSorted by age:")for record in sorted_records: print(f" {record[1]}: {record[3]} years old")
3. Coordinate System
# 2D points as tuplespoints = [ (0, 0), # Origin (3, 4), (1, 1), (5, 12), (8, 15)] def distance_from_origin(point): """Calculate distance from origin""" x, y = point return (x**2 + y**2) ** 0.5 # Calculate distancesprint("Distances from origin:")for point in points: dist = distance_from_origin(point) print(f" {point} -> {dist:.2f}") # Find closest point to originclosest = min(points, key=distance_from_origin)print(f"\nClosest point: {closest}") # Find farthest pointfarthest = max(points, key=distance_from_origin)print(f"Farthest point: {farthest}")
4. Function Return Multiple Values
def analyze_list(numbers): """Analyze list and return stats as tuple""" if not numbers: return None total = sum(numbers) count = len(numbers) average = total / count minimum = min(numbers) maximum = max(numbers) return (total, count, average, minimum, maximum) # Use the functiondata = [85, 90, 78, 92, 88, 76, 95]result = analyze_list(data) if result: total, count, avg, min_val, max_val = result print(f"Count: {count}") print(f"Total: {total}") print(f"Average: {avg:.2f}") print(f"Range: {min_val} - {max_val}")
5. Config Settings
# Configuration as tuple (immutable, prevent accidental changes)DATABASE_CONFIG = ( "localhost", # host 5432, # port "mydb", # database "user", # username "password" # password) # Unpack confighost, port, db, user, password = DATABASE_CONFIG def connect_database(): """Simulate database connection""" print(f"Connecting to {db} at {host}:{port}") print(f"Username: {user}") return f"Connected to {host}:{port}/{db}" connection = connect_database()print(connection) # ✅ Config không thể bị thay đổi accidentally# DATABASE_CONFIG[0] = "newhost" # TypeError!
Bài Tập Thực Hành
Bài 1: Tuple Operations
Tạo 2 tuples:
t1 = (1, 2, 3)t2 = (4, 5, 6)- Nối chúng
- Lặp lại t1 3 lần
- Kiểm tra 3 có trong t1 không
Bài 2: Swap Variables
Viết chương trình swap 3 variables:
a = 1, b = 2, c = 3- Sau swap:
a = 2, b = 3, c = 1
Bài 3: Statistics Function
Viết function get_stats(numbers) return tuple:
(min, max, average, median)
Bài 4: Point Distance
Cho list points (tuples), tìm:
- 2 điểm gần nhau nhất
- 2 điểm xa nhau nhất
Bài 5: Named Tuple
Tạo named tuple Student với:
- name, age, grade
- Tạo list 5 students
- Sort by grade
- Tìm student cao điểm nhất
Tóm Tắt
✅ Tuple: ordered, immutable, allow duplicates
✅ Syntax: () hoặc không cần ngoặc
✅ Single element: (5,) cần dấu phẩy
✅ Packing: point = 10, 20
✅ Unpacking: x, y = point
✅ Methods: count(), index()
✅ Faster và ít memory hơn list
✅ Named tuples: readable code
Bài Tiếp Theo
Bài 7: Dictionaries - Key-value pairs, dictionary methods, và dictionary comprehension.
Remember:
- Tuples are immutable - không thể thay đổi sau khi tạo
- Dùng tuple cho data cố định (coordinates, colors, config)
- Tuple unpacking rất mạnh mẽ và pythonic
- Named tuples cho code readable hơn
- Tuple nhanh hơn list - dùng khi không cần modify!