🎯 Mục tiêu bài học
Sau bài thực hành này, bạn sẽ:
✅ Thành thạo Variables, Data Types, Operators qua bài tập thực tế
✅ Viết được Control Flow và List Comprehension thành thạo
✅ Xây dựng Functions hoàn chỉnh cho các bài toán thực tế
✅ Sử dụng Collections hiệu quả trong xử lý dữ liệu
Thời gian: 2 giờ | Độ khó: Beginner → Intermediate | Yêu cầu: Hoàn thành Bài 1-2
🟢 Phần 1: Variables và Data Types (Easy)
Bài 1.1: Thông tin cá nhân
Tạo các biến lưu thông tin cá nhân, sau đó in ra card giới thiệu.
1# Tạo biến2name = "Nguyễn Văn A"3age = 224gpa = 3.755is_graduated = False6major = "Data Science"78# In card giới thiệu9print("=" * 40)10print(f"{'THÔNG TIN SINH VIÊN':^40}")11print("=" * 40)12print(f"Họ tên: {name}")13print(f"Tuổi: {age}")14print(f"GPA: {gpa:.2f}")15print(f"Ngành: {major}")16print(f"Tốt nghiệp: {'Rồi' if is_graduated else 'Chưa'}")17print("=" * 40)Bài 1.2: Chuyển đổi nhiệt độ
Đề bài: Viết code chuyển đổi Celsius ↔ Fahrenheit
1# Công thức: F = C × 9/5 + 322def celsius_to_fahrenheit(c):3 return c * 9/5 + 3245def fahrenheit_to_celsius(f):6 return (f - 32) * 5/978# Test9temps_c = [0, 25, 37, 100]10for c in temps_c:11 f = celsius_to_fahrenheit(c)12 print(f"{c}°C = {f:.1f}°F")1314# Output:15# 0°C = 32.0°F16# 25°C = 77.0°F17# 37°C = 98.6°F18# 100°C = 212.0°FBài 1.3: Tính BMI
Đề bài: Nhập cân nặng (kg) và chiều cao (m), tính BMI và phân loại
1def calculate_bmi(weight, height):2 bmi = weight / (height ** 2)3 4 if bmi < 18.5:5 category = "Gầy"6 elif bmi < 25:7 category = "Bình thường"8 elif bmi < 30:9 category = "Thừa cân"10 else:11 category = "Béo phì"12 13 return bmi, category1415# Test16weight, height = 70, 1.7517bmi, cat = calculate_bmi(weight, height)18print(f"BMI: {bmi:.1f} → {cat}")19# BMI: 22.9 → Bình thườngCheckpoint
Bạn đã hoàn thành 3 bài tập Easy chưa? Hãy chắc chắn bạn hiểu f-string, type conversion và ternary operator trước khi tiếp!
🟡 Phần 2: Control Flow và Loops (Medium)
Bài 2.1: FizzBuzz
Đề bài: In các số từ 1-30. Nếu chia hết cho 3 in "Fizz", chia hết cho 5 in "Buzz", cả hai in "FizzBuzz"
- Dùng vòng lặp
for i in range(1, 31) - Kiểm tra
i % 15 == 0TRƯỚC (vì chia hết cho cả 3 và 5) - Sau đó kiểm tra
i % 3 == 0, rồii % 5 == 0 - Thử viết bằng list comprehension sau khi làm được cách thường!
Giải pháp
1for i in range(1, 31):2 if i % 15 == 0:3 print("FizzBuzz")4 elif i % 3 == 0:5 print("Fizz")6 elif i % 5 == 0:7 print("Buzz")8 else:9 print(i)1011# List comprehension version12result = ["FizzBuzz" if i%15==0 else "Fizz" if i%3==0 13 else "Buzz" if i%5==0 else str(i) for i in range(1, 31)]Bài 2.2: Số nguyên tố
Đề bài: Tìm tất cả số nguyên tố trong khoảng 1-100
- Viết function
is_prime(n)trả về True/False - Số nguyên tố: chỉ chia hết cho 1 và chính nó
- Chỉ cần kiểm tra từ 2 đến √n (tối ưu!)
- Dùng list comprehension:
[n for n in range(2, 101) if is_prime(n)]
Giải pháp
1def is_prime(n):2 if n < 2:3 return False4 for i in range(2, int(n**0.5) + 1):5 if n % i == 0:6 return False7 return True89primes = [n for n in range(2, 101) if is_prime(n)]10print(f"Có {len(primes)} số nguyên tố: {primes}")11# Có 25 số nguyên tố: [2, 3, 5, 7, 11, 13, ...]Bài 2.3: Password Validator
Đề bài: Kiểm tra mật khẩu mạnh (≥8 ký tự, có chữ hoa, chữ thường, số, ký tự đặc biệt)
- Tạo list
errors = []để chứa lỗi - Kiểm tra
len(password) >= 8 - Dùng
any():any(c.isupper() for c in password)cho chữ hoa - Tương tự cho
.islower(),.isdigit(),not c.isalnum()(ký tự đặc biệt) - Trả về
(True, [])nếu OK,(False, errors)nếu có lỗi
Giải pháp
1def validate_password(password):2 errors = []3 4 if len(password) < 8:5 errors.append("Cần ít nhất 8 ký tự")6 if not any(c.isupper() for c in password):7 errors.append("Cần ít nhất 1 chữ hoa")8 if not any(c.islower() for c in password):9 errors.append("Cần ít nhất 1 chữ thường")10 if not any(c.isdigit() for c in password):11 errors.append("Cần ít nhất 1 chữ số")12 if not any(c in "!@#$%^&*" for c in password):13 errors.append("Cần ít nhất 1 ký tự đặc biệt (!@#$%^&*)")14 15 if errors:16 return False, errors17 return True, ["Mật khẩu hợp lệ! ✅"]1819# Test20passwords = ["abc", "Hello123", "Str0ng@Pass"]21for pw in passwords:22 valid, messages = validate_password(pw)23 print(f"\n'{pw}': {'✅' if valid else '❌'}")24 for msg in messages:25 print(f" - {msg}")Bài 2.4: Pattern Printing
- Tam giác: Mỗi dòng
icần(n-i)khoảng trắng +(2*i-1)dấu sao - Kim cương: Nửa trên = tam giác, nửa dưới = tam giác ngược (
range(n-1, 0, -1)) - Công thức:
" " * spaces + "*" * stars - Debug: In từng dòng với
iđể hiểu pattern
Đề bài: In tam giác sao và kim cương
1# Tam giác2n = 53for i in range(1, n + 1):4 print(" " * (n - i) + "*" * (2*i - 1))5# *6# ***7# *****8# *******9# *********1011# Kim cương12for i in range(1, n + 1):13 print(" " * (n - i) + "*" * (2*i - 1))14for i in range(n - 1, 0, -1):15 print(" " * (n - i) + "*" * (2*i - 1))Checkpoint
FizzBuzz là câu hỏi phỏng vấn kinh điển. Bạn đã giải được bằng cả for loop và list comprehension chưa?
🟡 Phần 3: Collections (Medium)
Bài 3.1: Word Counter
- Dùng
.lower()chuẩn hóa +.split()tách từ - Tạo dict rỗng, duyệt và
dict.get(word, 0) + 1 - Sắp xếp:
sorted(dict.items(), key=lambda x: x[1], reverse=True) - Hoặc dùng
collections.Counter(nhanh hơn!)
Đề bài: Đếm tần suất từ trong câu, sắp xếp từ phổ biến nhất
1def word_counter(text):2 words = text.lower().split()3 count = {}4 for word in words:5 word = word.strip(".,!?") # Bỏ dấu câu6 count[word] = count.get(word, 0) + 17 8 # Sắp xếp theo tần suất giảm dần9 sorted_words = sorted(count.items(), key=lambda x: x[1], reverse=True)10 return sorted_words1112text = "the cat sat on the mat the cat likes the mat"13result = word_counter(text)14for word, freq in result:15 print(f" '{word}': {freq} lần")16# 'the': 4 lần, 'cat': 2 lần, 'mat': 2 lần, ...Bài 3.2: Student Grade Manager
- Dùng closure — function trong function
students = {}là biến shared giữa các inner functions- Return dict các functions:
{"add": add_student, "avg": get_average, ...} - Rank:
sorted(students.items(), key=lambda x: sum(x[1])/len(x[1]), reverse=True)
Đề bài: Quản lý điểm sinh viên — thêm, xem, tính trung bình, xếp hạng
1def create_grade_manager():2 students = {}3 4 def add_student(name, scores):5 students[name] = scores6 7 def get_average(name):8 if name not in students:9 return None10 scores = students[name]11 return sum(scores) / len(scores)12 13 def get_ranking():14 averages = {name: sum(s)/len(s) for name, s in students.items()}15 return sorted(averages.items(), key=lambda x: x[1], reverse=True)16 17 def summary():18 ranking = get_ranking()19 print(f"\n{'Xếp hạng':^40}")20 print("-" * 40)21 for rank, (name, avg) in enumerate(ranking, 1):22 grade = "A" if avg >= 9 else "B" if avg >= 8 else "C" if avg >= 7 else "D"23 print(f" #{rank} {name:<15} TB: {avg:.1f} ({grade})")24 25 return add_student, get_average, get_ranking, summary2627# Sử dụng28add, avg, rank, summary = create_grade_manager()29add("Alice", [9, 8.5, 9.5, 10])30add("Bob", [7, 8, 7.5, 8])31add("Charlie", [10, 9, 9.5, 9])32summary()Bài 3.3: Set Operations — Phân tích khách hàng
- Intersection (
&): Khách hàng mua cả 3 kênh =online & store & mobile - Difference (
-): Chỉ mua online =online - store - mobile - Union (
|): Tổng khách unique =online | store | mobile - Set tự động loại bỏ duplicate, rất hữu dụng cho phân tích khách hàng!
Đề bài: Dùng Set để phân tích khách hàng mua từ nhiều kênh
1# Khách hàng theo kênh2online = {"Alice", "Bob", "Charlie", "Diana", "Eve"}3store = {"Bob", "Frank", "Charlie", "Grace"}4mobile = {"Alice", "Charlie", "Henry", "Diana"}56# Phân tích7print(f"Mua online: {len(online)} người")8print(f"Mua cửa hàng: {len(store)} người")9print(f"Mua mobile: {len(mobile)} người")1011# Mua tất cả kênh12all_channels = online & store & mobile13print(f"\nMua cả 3 kênh: {all_channels}") # {'Charlie'}1415# Chỉ mua online16only_online = online - store - mobile17print(f"Chỉ online: {only_online}") # {'Eve'}1819# Tổng khách hàng unique20total = online | store | mobile21print(f"Tổng unique: {len(total)} người") # 8Checkpoint
Bạn đã hiểu sự khác biệt giữa List, Dict, Set, Tuple chưa? Mỗi loại phù hợp với tình huống nào?
🔴 Phần 4: Functions Nâng Cao (Hard)
Bài 4.1: Flatten Nested List
- Dùng đệ quy (recursion): Function gọi lại chính nó
- Kiểm tra từng phần tử:
isinstance(item, list)→ gọi đệ quy, ngược lại → append - Base case: Khi
itemkhông phải list → dừng đệ quy - One-liner:
[x for sublist in lst for x in sublist](chỉ đệ quy 1 cấp)
Đề bài: Viết function biến list lồng nhau thành list phẳng (bất kỳ độ sâu)
1def flatten(lst):2 """Flatten list lồng nhau bất kỳ"""3 result = []4 for item in lst:5 if isinstance(item, list):6 result.extend(flatten(item)) # Đệ quy7 else:8 result.append(item)9 return result1011# Test12nested = [1, [2, 3], [4, [5, 6]], [7, [8, [9]]]]13print(flatten(nested))14# [1, 2, 3, 4, 5, 6, 7, 8, 9]1516# One-liner (chỉ 1 cấp)17simple = [[1,2], [3,4], [5]]18flat = [x for sublist in simple for x in sublist]19# [1, 2, 3, 4, 5]Bài 4.2: Decorator — Logging & Retry
- Decorator: Function nhận function, trả về function mới có thêm logic
- Pattern:
def decorator(func): def wrapper(*args, **kwargs): ... return func(*args, **kwargs) return wrapper @wraps(func): Giữ nguyên metadata (__name__,__doc__)- Retry: Dùng loop + try/except,
time.sleep(delay)giữa các lần thử - Decorator này rất quan trọng khi làm API calls hoặc database connections!
Đề bài: Viết 2 decorators: @log ghi lại mọi lần gọi, @retry thử lại khi lỗi
1import time2from functools import wraps34def log(func):5 """Decorator ghi log mỗi lần gọi function"""6 @wraps(func)7 def wrapper(*args, **kwargs):8 args_str = ", ".join(map(str, args))9 kwargs_str = ", ".join(f"{k}={v}" for k, v in kwargs.items())10 all_args = ", ".join(filter(None, [args_str, kwargs_str]))11 print(f"📝 Calling {func.__name__}({all_args})")12 result = func(*args, **kwargs)13 print(f"✅ {func.__name__} returned: {result}")14 return result15 return wrapper1617def retry(max_attempts=3, delay=1):18 """Decorator thử lại khi gặp lỗi"""19 def decorator(func):20 @wraps(func)21 def wrapper(*args, **kwargs):22 for attempt in range(1, max_attempts + 1):23 try:24 return func(*args, **kwargs)25 except Exception as e:26 print(f"⚠️ Attempt {attempt}/{max_attempts} failed: {e}")27 if attempt < max_attempts:28 time.sleep(delay)29 raise Exception(f"Failed after {max_attempts} attempts")30 return wrapper31 return decorator3233# Test34@log35def add(a, b):36 return a + b3738@retry(max_attempts=3, delay=0.5)39def risky_operation():40 import random41 if random.random() < 0.7:42 raise ConnectionError("Network timeout")43 return "Success!"4445add(3, 5)46risky_operation()Bài 4.3: Mini Data Pipeline
- Pipeline ETL: Extract (load) → Transform (clean) → Load (analyze)
- Mỗi function chỉ làm 1 việc (Single Responsibility Principle)
- Truyền output function này làm input function khác:
clean_data(load_data(raw)) - Validation: Kiểm tra
len(row) == len(header)vàall(cell.strip()) - Pattern này giống Pandas pipeline:
df.pipe(clean).pipe(transform).pipe(analyze)
Đề bài: Xây dựng pipeline xử lý dữ liệu dùng functions
1def load_data(raw_data):2 """Bước 1: Parse raw data"""3 return [row.split(",") for row in raw_data.strip().split("\n")]45def clean_data(data):6 """Bước 2: Clean & validate"""7 header = data[0]8 cleaned = []9 for row in data[1:]:10 if len(row) == len(header) and all(cell.strip() for cell in row):11 cleaned.append([cell.strip() for cell in row])12 return header, cleaned1314def transform_data(header, data):15 """Bước 3: Convert to list of dicts"""16 return [dict(zip(header, row)) for row in data]1718def analyze(records):19 """Bước 4: Tính thống kê"""20 scores = [float(r["score"]) for r in records]21 return {22 "count": len(scores),23 "mean": sum(scores) / len(scores),24 "min": min(scores),25 "max": max(scores),26 }2728# Pipeline29raw = """name,score,grade30Alice,95,A31Bob,87,B32Charlie,92,A33Diana,,B34Eve,78,C"""3536data = load_data(raw)37header, cleaned = clean_data(data)38records = transform_data(header, cleaned)39stats = analyze(records)4041print(f"Kết quả phân tích ({stats['count']} records):")42print(f" Mean: {stats['mean']:.1f}")43print(f" Min: {stats['min']:.0f} | Max: {stats['max']:.0f}")Bài 4.3 mô phỏng pipeline ETL (Extract-Transform-Load) thực tế mà Data Engineer/Scientist hay làm. Bạn sẽ gặp lại pattern này khi học Pandas!
Checkpoint
Bạn đã giải được ít nhất 1 bài Hard chưa? Bài flatten dùng đệ quy — hãy đảm bảo bạn hiểu cách hoạt động.
📝 Tổng Kết
Câu hỏi tự kiểm tra
- Trong bài FizzBuzz, bạn cần kiểm tra điều kiện chia hết cho 15 trước hay chia hết cho 3/5 trước? Tại sao?
- Pipeline ETL gồm những bước nào? Tại sao mỗi function chỉ nên làm một việc?
- Khi viết hàm kiểm tra mật khẩu, những tiêu chí nào cần kiểm tra và bạn dùng string methods nào?
- Đệ quy (recursion) hoạt động như thế nào trong bài flatten nested list? Base case là gì?
✅ Checklist hoàn thành
- 🟢 Phần 1 (Easy): Variables, f-string, BMI calculator — xong?
- 🟡 Phần 2 (Medium): FizzBuzz, prime numbers, password validator — xong?
- 🟡 Phần 3 (Medium): Word counter, grade manager, set operations — xong?
- 🔴 Phần 4 (Hard): Flatten, decorators, data pipeline — xong?
Bảng đánh giá
| Mức độ | Hoàn thành | Đánh giá |
|---|---|---|
| Phần 1 (Easy) | 3/3 bài | Sẵn sàng tiếp tục ✅ |
| Phần 2 (Medium) | ≥ 3/4 bài | Nền tảng vững |
| Phần 3 (Medium) | ≥ 2/3 bài | Hiểu Collections tốt |
| Phần 4 (Hard) | ≥ 1/3 bài | Xuất sắc! 🌟 |
Bài tiếp theo: NumPy — Nền tảng tính toán khoa học trong Python! 🔢
