Mục tiêu

Là một người mới hoàn toàn, mục tiêu của bạn nên là:

  • Làm quen với các khái niệm cơ bản (biến, vòng lặp, điều kiện, hàm).
  • Luyện tập hơn 30 bài tập lập trình.
  • Xây dựng 2 dự án để áp dụng các khái niệm đã học.
  • Làm quen với ít nhất 2 framework.
  • Bắt đầu với IDE, GitHub, hosting, dịch vụ, v.v.

Sau khi hoàn thành, bạn sẽ trở thành một Junior Python Developer.


Kế hoạch tổng thể:

Bây giờ, mình sẽ chia kế hoạch theo từng tuần cho bạn.

1. Làm quen với Python

Hãy tò mò và khám phá cách làm việc với Python. Hãy kiểm tra càng nhiều thứ càng tốt.

  • Ngày 1: 4 khái niệm cơ bản (3 giờ) → print, biến, nhập dữ liệu, câu lệnh điều kiện (if-else).
  • Ngày 2: 4 khái niệm cơ bản (5 giờ) → List, vòng lặp for, vòng lặp while, hàm, import module.
  • Ngày 3: Bài tập lập trình đơn giản (4 giờ) → Hoán đổi hai biến, chuyển đổi độ C sang độ F, tính tổng các chữ số của một số, kiểm tra số nguyên tố, tạo số ngẫu nhiên, loại bỏ phần tử trùng lặp trong danh sách, v.v.
  • Ngày 4: Bài tập lập trình trung bình (5 giờ) → Đảo ngược chuỗi (kiểm tra palindrome), tính GCD, gộp hai mảng đã sắp xếp, trò chơi đoán số, tính tuổi, v.v.
  • Ngày 5: Cấu trúc dữ liệu (4 giờ) → Stack, Queue, Dictionary, Tuple, Cây, Danh sách liên kết.
  • Ngày 6: Lập trình hướng đối tượng - OOP (5 giờ) → Đối tượng, Lớp, Phương thức & Constructor, Kế thừa trong OOP.
  • Ngày 7: Thuật toán (5 giờ) → Tìm kiếm (Tìm kiếm tuyến tính và nhị phân), Sắp xếp (Bubble sort, Selection sort), Đệ quy (tính giai thừa, dãy Fibonacci), Độ phức tạp thời gian (Tuyến tính, Bậc hai, Hằng số).

2. Bắt đầu phát triển phần mềm (Xây dựng dự án)

Hãy tập hợp mọi thứ lại để tạo ra một dự án thực tế.

  • Ngày 1: Làm quen với IDE (4 giờ) → Chọn một IDE tốt, mình khuyên dùng VS Code và cài đặt Python Extension.
  • Ngày 2: Tìm hiểu về GitHub (5 giờ) → Tạo repository, thử commit, diff, push code. Học về branch, merge, pull request.
  • Ngày 3: Dự án đầu tiên - Máy tính đơn giản (4 giờ) → Làm quen với Tkinter, tạo một ứng dụng máy tính đơn giản.
  • Ngày 4-6: Dự án cá nhân (5 giờ mỗi ngày) → Chọn một dự án và bắt đầu làm. Nếu chưa có ý tưởng, hãy tham khảo danh sách dự án Python thú vị.
  • Ngày 7: Hosting (5 giờ) → Học cách triển khai dự án lên server, tạo tài khoản Heroku và deploy ứng dụng.

Tại sao phải làm dự án?

  • Học qua video hay khóa học không đủ! Bạn cần áp dụng kiến thức vào thực tế.
  • Làm dự án giúp bạn suy nghĩ và giải quyết vấn đề, chứ không chỉ làm theo hướng dẫn.

3. Làm quen với quy trình phát triển phần mềm

Tuần thứ 3 sẽ giúp bạn hiểu về quy trình phát triển phần mềm. Bạn không cần thành thạo tất cả, nhưng hãy biết những điều cơ bản.

  • Ngày 1: Cơ bản về Cơ sở dữ liệu (4 giờ) → Câu lệnh SQL (CREATE TABLE, SELECT, WHERE, UPDATE), các hàm SQL (AVG, MAX, COUNT), chuẩn hóa dữ liệu, JOIN (INNER JOIN, OUTER JOIN).
  • Ngày 2: Sử dụng Cơ sở dữ liệu với Python (5 giờ) → Làm việc với SQLite hoặc Pandas, kết nối, tạo bảng, thêm dữ liệu, đọc dữ liệu.
  • Ngày 3: API (5 giờ) → Cách gọi API, học về JSON, microservices, REST API.
  • Ngày 4: Numpy (4 giờ) → Làm quen với thư viện Numpy, thực hành 30 bài tập cơ bản.
  • Ngày 5-6: Xây dựng Website Portfolio (5 giờ mỗi ngày) → Học Django, xây dựng website cá nhân, tìm hiểu Flask.
  • Ngày 7: Kiểm thử và Debugging (4 giờ) → Học về Unit Test (PyTest), Logging, Debugging.

4. Chuẩn bị nghiêm túc để xin việc

Mục tiêu tuần 4 là tìm việc làm hoặc ít nhất là chuẩn bị tốt cho tương lai.

  • Ngày 1: Viết CV (5 giờ) → Tạo một CV một trang, đưa phần tóm tắt kỹ năng lên đầu, liệt kê các dự án có link GitHub.
  • Ngày 2: Website Portfolio (6 giờ) → Viết ít nhất 2 bài blog và thêm vào website cá nhân.
  • Ngày 3: Tạo tài khoản LinkedIn → Cập nhật tất cả thông tin từ CV lên LinkedIn.
  • Ngày 4: Chuẩn bị phỏng vấn → Tìm hiểu câu hỏi phỏng vấn, luyện tập 10 bài tập thuật toán trên giấy, tìm câu hỏi trên Glassdoor, Careercup.
  • Ngày 5: Mở rộng mối quan hệ → Tham gia các sự kiện Meetup, hội chợ việc làm, gặp gỡ nhà tuyển dụng.
  • Ngày 6: Bắt đầu ứng tuyển → Google "Python Jobs", chọn 3 công việc, tùy chỉnh CV cho từng vị trí. Tìm 2-3 kỹ năng chưa biết trong mô tả công việc và học trong 3-4 ngày tiếp theo.
  • Ngày 7: Học từ thất bại → Nếu bị từ chối, hãy tìm ra 2 kỹ năng bạn cần cải thiện và dành 4-5 ngày để học thật sâu.

Sự thật về việc tìm việc

  • Bạn sẽ không bao giờ cảm thấy 100% sẵn sàng để đi làm.
  • Bạn không cần biết mọi thứ, chỉ cần thành thạo một vài kỹ năng chính và làm quen với các công nghệ khác.

Tận hưởng quá trình học tập

  • Học là một quá trình! Bạn sẽ gặp khó khăn, nhưng càng cố gắng, bạn sẽ càng giỏi hơn.
  • Nếu bạn hoàn thành 100% kế hoạch này trong 28 ngày, thật tuyệt! Nhưng nếu chỉ hoàn thành 60-70%, bạn vẫn đủ khả năng trở thành một lập trình viên giỏi.

👉 Bạn có thể học từ đâu?

  • Programming Hero (ứng dụng học Python thú vị)
  • Learn Python for Data Science (khóa học online miễn phí)
  • Chuỗi video trên YouTube

Chúc bạn thành công trên hành trình học Python! 🚀

Bước 1: Làm quen với Python

Ngày 1: Nền Tảng Cơ Bản (5 giờ)

Mục tiêu: Làm quen với Python, cách nhập/xuất dữ liệu và kiểm soát luồng cơ bản.

Giờ 1: print() và Biến (Variable) - 60 phút

Lý thuyết (20 phút)

  • print(): In dữ liệu (chuỗi, số, biến).
  • Biến: Lưu giá trị, không cần khai báo kiểu.

Thực hành (30 phút)

name = "Alex"
age = 25
print(f"Hi, I’m {name}, {age} years old.")

Bài tập (10 phút)

Tạo 3 biến (tên, tuổi, thành phố) và in thành câu hoàn chỉnh.


Giờ 2: input() - 60 phút

Lý thuyết (20 phút)

  • Lấy dữ liệu người dùng.
  • Chuyển kiểu (int(), float()).

Thực hành (30 phút)

name = input("Your name: ")
age = int(input("Your age: "))
print(f"In 10 years, {name} will be {age + 10}.")

Bài tập (10 phút)

Hỏi năm sinh, tính tuổi (2025 - năm sinh).


Giờ 3: Câu lệnh điều kiện (if-else) - 60 phút

Lý thuyết (20 phút)

  • Kiểm tra điều kiện với if, elif, else.

Thực hành (30 phút)

age = int(input("Enter age: "))
if age >= 18:
    print("Adult")
else:
    print("Minor")

Bài tập (10 phút)

Hỏi số, kiểm tra chẵn/lẻ (dùng %).


Giờ 4: Chuỗi (Strings) - 60 phút

Lý thuyết (20 phút)

  • Cắt chuỗi, phương thức (upper(), lower(), len()).

Thực hành (30 phút)

text = "python"
print(text[0:3])  # pyt
print(text.upper())  # PYTHON
print(len(text))  # 6

Bài tập (10 phút)

Hỏi tên, in chữ cái đầu in hoa.


Giờ 5: Toán tử (Operators) - 60 phút

Lý thuyết (20 phút)

  • Toán tử số học (+, -, *, /, %).
  • Toán tử so sánh (==, <).
  • Toán tử logic (and, or).

Thực hành (30 phút)

a = 10
b = 3
print(a + b, a % b)  # 13 1
print(a > b and b != 0)  # True

Bài tập (10 phút)

Hỏi 2 số, in tổng và kiểm tra số nào lớn hơn.


Kết quả Ngày 1

Bạn hiểu cách nhập/xuất dữ liệu, lưu trữ, thao tác chuỗi và ra quyết định cơ bản.


Lưu Ý

  • Thực hành: Chạy code từng phần, thử thay đổi giá trị.
  • Ôn tập: Cuối mỗi ngày, viết lại 1-2 ví dụ không nhìn code.
  • Công cụ: Dùng IDLE, VS Code hoặc Google Colab để chạy.

Ngày 2: Tăng Cường và Tổ Chức Code (5 giờ)

Mục tiêu: Làm việc với dữ liệu phức tạp hơn, tự động hóa và tổ chức code.

Giờ 1: Vòng lặp (Loops) - 60 phút

Lý thuyết (20 phút)

  • for (lặp qua dãy).
  • while (lặp theo điều kiện).

Thực hành (30 phút)

for i in range(1, 4):
    print(i)  # 1 2 3

count = 0
while count < 3:
    print(count)
    count += 1

Bài tập (10 phút)

In bảng cửu chương 3.


Giờ 2: Danh sách (Lists) - 60 phút

Lý thuyết (20 phút)

  • Tạo, thêm (append()), xóa (pop()), truy cập ([index]).

Thực hành (30 phút)

numbers = [1, 2, 3]
numbers.append(4)
print(numbers)  # [1, 2, 3, 4]

for num in numbers:
    print(num * 2)  # 2 4 6 8

Bài tập (10 phút)

Hỏi 3 số, lưu vào danh sách, in tổng.


Giờ 3: Hàm (Functions) - 60 phút

Lý thuyết (20 phút)

  • Định nghĩa hàm, tham số, return.

Thực hành (30 phút)

def square(num):
    return num * num

print(square(5))  # 25

Bài tập (10 phút)

Viết hàm kiểm tra số nguyên tố.


Giờ 4: Kiểu dữ liệu khác (Tuple, Dictionary, Set) - 60 phút

Lý thuyết (20 phút)

  • Tuple: Không thay đổi ((1, 2)).
  • Dictionary: Key-value ({"key": "value"}).
  • Set: Không trùng ({1, 2, 3}).

Thực hành (30 phút)

point = (3, 4)
info = {"name": "Alex", "age": 25}
unique = {1, 1, 2}

print(point[0], info["name"], unique)  # 3 Alex {1, 2}

Bài tập (10 phút)

Tạo dictionary lưu tên và điểm 3 môn, in điểm trung bình.


Giờ 5: Xử lý lỗi (Try-Except) và Ôn tập - 60 phút

Lý thuyết (15 phút)

  • Dùng try-except để xử lý lỗi.

Thực hành (25 phút)

try:
    num = int(input("Enter a number: "))
    print(num / 2)
except ValueError:
    print("Invalid input!")

Dự án nhỏ (20 phút)

Viết chương trình:

  1. Hỏi tên, tuổi, 3 điểm môn học (dùng input()).
  2. Lưu vào dictionary.
  3. Dùng hàm tính điểm trung bình.
  4. Kiểm tra tuổi > 18 để in "Adult".
  5. Dùng try-except để xử lý lỗi nhập liệu.

Kết quả Ngày 2

Bạn biết tự động hóa, quản lý dữ liệu, viết code tái sử dụng và xử lý lỗi.


Tóm Tắt Khái Niệm Đã Bao Phủ

Ngày 1:

  • print(), biến, input(), if-else, chuỗi, toán tử.

Ngày 2:

  • Vòng lặp, danh sách, hàm, tuple/dictionary/set, xử lý lỗi.

Ngày 3: Lập trình đơn giản

1. Hoán đổi hai biến

a = 5
b = 10

# Hoán đổi
a, b = b, a

print("Sau khi hoán đổi: a =", a, ", b =", b)

Bài toán hoán đổi hai biến thường được sử dụng trong lập trình để thay đổi giá trị của hai biến mà không làm mất dữ liệu. Dưới đây là một số tình huống thực tế mà hoán đổi hai biến có thể hữu ích:


Sắp xếp danh sách (Sorting Algorithms)
Khi triển khai các thuật toán sắp xếp như Bubble Sort, Selection Sort, Quick Sort, ta thường cần hoán đổi vị trí của các phần tử để đưa danh sách về thứ tự mong muốn.
Ví dụ:

arr = [3, 1, 2]
arr[0], arr[1] = arr[1], arr[0]  # Hoán đổi 3 và 1
print(arr)  # Output: [1, 3, 2]


Hoán đổi giá trị trong trò chơi (Game Development)
Khi lập trình game, ta có thể cần hoán đổi vị trí của hai đối tượng, ví dụ như hai quân cờ trong một bàn cờ.



Đổi chỗ hai biến trong thuật toán đệ quy
Một số bài toán đệ quy yêu cầu hoán đổi hai giá trị để xử lý thuận tiện hơn, như trong đảo ngược mảng.
Ví dụ:

def reverse_list(lst, start, end):
    if start >= end:
        return
    lst[start], lst[end] = lst[end], lst[start]  # Hoán đổi
    reverse_list(lst, start + 1, end - 1)

arr = [1, 2, 3, 4, 5]
reverse_list(arr, 0, len(arr) - 1)
print(arr)  # Output: [5, 4, 3, 2, 1]


Hoán đổi giá trị trong thuật toán tìm kiếm tối ưu
Khi thực hiện tối ưu hóa bộ nhớ hoặc thuật toán tìm kiếm, việc hoán đổi hai biến có thể giúp giảm chi phí tính toán.

Ví dụ: Hoán đổi giá trị nhỏ nhất lên đầu danh sách

arr = [4, 2, 9, 1]
min_index = arr.index(min(arr))
arr[0], arr[min_index] = arr[min_index], arr[0]  # Đưa giá trị nhỏ nhất về đầu danh sách
print(arr)  # Output: [1, 2, 9, 4]


Đảo ngược giá trị hai biến trong xử lý dữ liệu
Ví dụ: Đổi tiền tệ giữa hai loại ví trong ứng dụng tài chính

wallet_a = 100  # Ví A có 100 đô
wallet_b = 50   # Ví B có 50 đô

wallet_a, wallet_b = wallet_b, wallet_a  # Đổi giá trị

print(wallet_a, wallet_b)  # Output: 50 100

Tóm lại

Hoán đổi hai biến không chỉ là một bài tập đơn giản mà còn rất hữu ích trong các thuật toán, xử lý dữ liệu, và lập trình game.


2. Chuyển đổi độ C sang độ F

Chuyển đổi độ C sang độ F để làm gì?

Việc chuyển đổi từ độ C (Celsius, °C) sang độ F (Fahrenheit, °F) rất quan trọng trong nhiều lĩnh vực như khoa học, y tế, hàng không, dự báo thời tiết và du lịch.

Công thức chuyển đổi:

\[ °F = (°C \times \frac{9}{5}) + 32 \]


Ứng dụng thực tế của chuyển đổi nhiệt độ

🔹 Dự báo thời tiết

  • Ở Mỹ, Canada hoặc các nước sử dụng đơn vị Fahrenheit, khi đọc dự báo thời tiết của một quốc gia khác sử dụng độ C (ví dụ: Việt Nam, châu Âu), ta cần chuyển đổi giữa hai hệ đơn vị.

🔹 Y tế (đo thân nhiệt)

  • Một số nhiệt kế sử dụng °C, trong khi ở Mỹ dùng °F. Ví dụ:
    • 37°C = 98.6°F (nhiệt độ cơ thể bình thường).
    • 40°C = 104°F (sốt cao, cần cấp cứu).

🔹 Hàng không và khoa học

  • Trong ngành hàng không, các hệ thống đo lường thường cần chuyển đổi giữa °C và °F để đảm bảo tính đồng nhất trên toàn cầu.

🔹 Du lịch & nấu ăn

  • Nếu bạn du lịch đến Mỹ và thấy công thức nấu ăn yêu cầu lò nướng ở 350°F, bạn có thể cần chuyển đổi sang °C để sử dụng lò của mình (~176°C).
def c_to_f(celsius):
    return (celsius * 9/5) + 32

c = float(input("Nhập độ C: "))
print(f"{c}°C = {c_to_f(c)}°F")

3. Tính tổng các chữ số của một số

Tính tổng các chữ số của một số để làm gì?

Việc tính tổng các chữ số của một số có nhiều ứng dụng trong toán học, mã hóa, kiểm tra số đặc biệttính toán số học. Một số ứng dụng cụ thể:

🔹 Kiểm tra số Harshad (Niven Number)

  • Một số chia hết cho tổng các chữ số của nó được gọi là số Harshad.
  • Ví dụ: 18 → ( 1 + 8 = 9 ), mà ( 18 \div 9 = 2 ) (chia hết) → 18 là số Harshad.

🔹 Kiểm tra số may mắn (Lucky Number)

  • Một số có tổng chữ số bằng 7 hoặc 9 thường được coi là may mắn trong nhiều nền văn hóa.

🔹 Xác minh số trong kiểm tra tính hợp lệ

  • Một số hệ thống số serial hoặc mã vạch sử dụng tổng các chữ số như một phần kiểm tra lỗi (checksum).

Cách tính tổng các chữ số của một số trong Python

Dưới đây là cách viết chương trình Python để tính tổng các chữ số của một số nguyên dương hoặc âm.

Cách 1: Chuyển số thành chuỗi rồi tính tổng

def sum_of_digits(n):
    return sum(int(digit) for digit in str(abs(n)))  # abs() để xử lý số âm

num = int(input("Nhập số nguyên: "))
print("Tổng các chữ số:", sum_of_digits(num))

📌 Ví dụ chạy chương trình

Nhập số nguyên: 1234
Tổng các chữ số: 10
Nhập số nguyên: -567
Tổng các chữ số: 18

Cách 2: Dùng vòng lặp (không chuyển sang chuỗi)

def sum_of_digits(n):
    n = abs(n)  # Bỏ dấu âm nếu có
    total = 0
    while n > 0:
        total += n % 10  # Lấy chữ số cuối cùng
        n //= 10  # Bỏ chữ số cuối cùng
    return total

num = int(input("Nhập số nguyên: "))
print("Tổng các chữ số:", sum_of_digits(num))

4. Kiểm tra số nguyên tố

Kiểm tra số nguyên tố để làm gì?

Số nguyên tố là một khái niệm quan trọng trong toán học, mật mã học, khoa học máy tínhcác thuật toán số học. Một số ứng dụng thực tế của việc kiểm tra số nguyên tố:

🔹 Mật mã RSA (Bảo mật thông tin)

  • Hệ thống mã hóa RSA dựa trên hai số nguyên tố rất lớn để tạo khóa bảo mật.

🔹 Thuật toán phân tích số nguyên

  • Một số thuật toán như Fermat’s Factorization cần kiểm tra số nguyên tố để tìm ước số lớn nhất.

🔹 Tối ưu hóa thuật toán tìm số nguyên tố lớn

  • Trong các bài toán lớn như Tìm số nguyên tố Mersenne, kiểm tra số nguyên tố rất quan trọng.

Cách kiểm tra số nguyên tố trong Python

Cách 1: Dùng vòng lặp cơ bản

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, n):  # Duyệt từ 2 đến n-1
        if n % i == 0:
            return False
    return True

num = int(input("Nhập số nguyên: "))
print(f"{num} là số nguyên tố" if is_prime(num) else f"{num} không phải số nguyên tố")

📌 Ví dụ chạy chương trình:

Nhập số nguyên: 7
7 là số nguyên tố
Nhập số nguyên: 10
10 không phải số nguyên tố

Cách 2: Cải thiện hiệu suất (Chỉ kiểm tra đến √n)

💡 Tối ưu: Một số ( n ) không phải số nguyên tố nếu nó có ước số nhỏ hơn hoặc bằng ( \sqrt{n} ).

import math

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):  # Chỉ kiểm tra đến √n
        if n % i == 0:
            return False
    return True

num = int(input("Nhập số nguyên: "))
print(f"{num} là số nguyên tố" if is_prime(num) else f"{num} không phải số nguyên tố")

🚀 Tối ưu hơn nhiều, đặc biệt với số lớn!


Cách 3: Kiểm tra số nguyên tố nhanh hơn (Bỏ qua số chẵn, chỉ kiểm tra số lẻ)

💡 Tối ưu hơn nữa:

  • Nếu ( n ) chia hết cho 2 hoặc 3, thì không cần kiểm tra nữa.
  • Kiểm tra các số có dạng ( 6k ± 1 ) giúp giảm số lần lặp.
import math

def is_prime(n):
    if n < 2:
        return False
    if n in (2, 3):
        return True
    if n % 2 == 0 or n % 3 == 0:
        return False
    for i in range(5, int(math.sqrt(n)) + 1, 6):  # Kiểm tra 6k ± 1
        if n % i == 0 or n % (i + 2) == 0:
            return False
    return True

num = int(input("Nhập số nguyên: "))
print(f"{num} là số nguyên tố" if is_prime(num) else f"{num} không phải số nguyên tố")

Mở rộng bài toán

  • Tìm tất cả số nguyên tố trong khoảng từ 1 đến N?
  • Sinh số nguyên tố ngẫu nhiên?
  • Tính tổng các số nguyên tố trong một danh sách?

5. Tạo số ngẫu nhiên

Tạo số ngẫu nhiên để làm gì?

Việc tạo số ngẫu nhiên rất quan trọng trong nhiều lĩnh vực như:

🔹 Bảo mật & Mật mã học → Dùng trong mã OTP, tạo khóa bảo mật.
🔹 Trò chơi & Mô phỏng → Dùng để tạo quái vật, bài ngẫu nhiên, mô phỏng vật lý.
🔹 Thuật toán & Machine Learning → Khởi tạo dữ liệu ngẫu nhiên cho AI.
🔹 Thống kê & Khoa học dữ liệu → Tạo mẫu thử để kiểm tra mô hình.


Cách tạo số ngẫu nhiên trong Python

Python có thư viện random để tạo số ngẫu nhiên.

1️⃣ Tạo số nguyên ngẫu nhiên trong một khoảng

import random

num = random.randint(1, 100)  # Số ngẫu nhiên từ 1 đến 100
print("Số ngẫu nhiên:", num)

📌 Ví dụ Output:

Số ngẫu nhiên: 42

2️⃣ Tạo số thực ngẫu nhiên giữa hai giá trị

num = random.uniform(1.5, 10.5)  # Số thực ngẫu nhiên từ 1.5 đến 10.5
print("Số thực ngẫu nhiên:", num)

📌 Ví dụ Output:

Số thực ngẫu nhiên: 6.2783

3️⃣ Tạo số ngẫu nhiên từ danh sách cho trước

choices = [10, 20, 30, 40, 50]
num = random.choice(choices)  # Lấy ngẫu nhiên một phần tử từ danh sách
print("Số ngẫu nhiên:", num)

📌 Ví dụ Output:

Số ngẫu nhiên: 30

4️⃣ Tạo danh sách số ngẫu nhiên

random_numbers = [random.randint(1, 100) for _ in range(5)]  # Danh sách 5 số ngẫu nhiên
print("Danh sách số ngẫu nhiên:", random_numbers)

📌 Ví dụ Output:

Danh sách số ngẫu nhiên: [23, 76, 12, 89, 45]

5️⃣ Xáo trộn danh sách ngẫu nhiên

numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)  # Xáo trộn danh sách
print("Danh sách sau khi xáo trộn:", numbers)

📌 Ví dụ Output:

Danh sách sau khi xáo trộn: [3, 1, 5, 2, 4]

Mở rộng bài toán

  • Sinh mật khẩu ngẫu nhiên? 🔐
  • Sinh số nguyên tố ngẫu nhiên?
  • Sinh dữ liệu giả (Fake Data) để kiểm thử?

6. Loại bỏ phần tử trùng lặp trong danh sách

def remove_duplicates(lst):
    return list(set(lst))

numbers = [1, 2, 2, 3, 4, 4, 5]
print("Danh sách sau khi loại bỏ trùng lặp:", remove_duplicates(numbers))

Dưới đây là các bài tập lập trình đơn giản đã được bổ sung công dụng ứng dụng thực tế cho từng bài để bạn thấy được giá trị của chúng trong đời sống hoặc lập trình. Mỗi bài vẫn giữ nguyên tính đơn giản, phù hợp để học Python trong khoảng 4 giờ tổng cộng.

1. Hoán đổi hai biến

Mô tả: Nhập hai số từ người dùng và hoán đổi giá trị của chúng mà không dùng biến tạm.
Thời gian: ~30 phút
Mã mẫu:

a = float(input("Nhập số thứ nhất: "))
b = float(input("Nhập số thứ hai: "))
print(f"Trước khi hoán đổi: a = {a}, b = {b}")
a, b = b, a
print(f"Sau khi hoán đổi: a = {a}, b = {b}")

Công dụng thực tế:

  • Trong thuật toán sắp xếp (như Bubble Sort), hoán đổi giá trị là thao tác cơ bản để sắp xếp danh sách (ví dụ: sắp xếp điểm số học sinh từ thấp đến cao).
  • Ứng dụng trong xử lý dữ liệu khi cần đổi chỗ hai giá trị trong bộ nhớ mà không tốn thêm không gian.

2. Chuyển đổi độ C sang độ F

Mô tả: Nhập nhiệt độ (°C) và chuyển đổi sang độ F bằng công thức: °F = °C * 9/5 + 32.
Thời gian: ~30 phút
Mã mẫu:

celsius = float(input("Nhập nhiệt độ (°C): "))
fahrenheit = celsius * 9/5 + 32
print(f"{celsius}°C tương đương {fahrenheit}°F")

Công dụng thực tế:

  • Dùng trong ứng dụng thời tiết để chuyển đổi nhiệt độ giữa các đơn vị cho người dùng ở các quốc gia khác nhau (Mỹ dùng °F, Việt Nam dùng °C).
  • Hỗ trợ kỹ sư hoặc nhà khoa học chuyển đổi nhanh dữ liệu nhiệt độ khi làm việc với thiết bị quốc tế.

3. Tính tổng các chữ số của một số

Mô tả: Nhập một số nguyên và tính tổng các chữ số của nó (ví dụ: 123 → 1 + 2 + 3 = 6).
Thời gian: ~45 phút
Mã mẫu:

number = int(input("Nhập một số nguyên: "))
total = 0
while number > 0:
    total += number % 10
    number //= 10
print(f"Tổng các chữ số: {total}")

Công dụng thực tế:

  • Kiểm tra tính hợp lệ của mã số (như số ISBN của sách hoặc mã vạch) bằng cách tính tổng chữ số và so sánh với quy tắc.
  • Ứng dụng trong trò chơi hoặc bài toán giải đố liên quan đến số học.

4. Kiểm tra số nguyên tố

Mô tả: Nhập một số nguyên và kiểm tra xem nó có phải số nguyên tố không.
Thời gian: ~1 giờ
Mã mẫu:

num = int(input("Nhập một số nguyên: "))
if num < 2:
    print(f"{num} không phải số nguyên tố.")
else:
    is_prime = True
    for i in range(2, int(num ** 0.5) + 1):
        if num % i == 0:
            is_prime = False
            break
    if is_prime:
        print(f"{num} là số nguyên tố.")
    else:
        print(f"{num} không phải số nguyên tố.")

Công dụng thực tế:

  • Dùng trong mật mã học (như thuật toán RSA) để tạo khóa mã hóa dựa trên số nguyên tố lớn.
  • Ứng dụng trong kiểm tra tính tối ưu của các bài toán phân bổ tài nguyên (ví dụ: chia công việc không bị trùng lặp).

5. Tạo số ngẫu nhiên

Mô tả: Tạo một số ngẫu nhiên trong khoảng do người dùng nhập (min và max).
Thời gian: ~30 phút
Mã mẫu:

import random
min_val = int(input("Nhập giá trị nhỏ nhất: "))
max_val = int(input("Nhập giá trị lớn nhất: "))
random_num = random.randint(min_val, max_val)
print(f"Số ngẫu nhiên: {random_num}")

Công dụng thực tế:

  • Dùng trong trò chơi (như xúc xắc, rút thăm may mắn) để tạo kết quả ngẫu nhiên.
  • Ứng dụng trong mô phỏng (simulation) để thử nghiệm các kịch bản (ví dụ: mô phỏng giao thông hoặc dự đoán thời tiết).

6. Loại bỏ phần tử trùng lặp trong danh sách

Mô tả: Nhập danh sách số (cách nhau bằng dấu cách) và loại bỏ các phần tử trùng lặp.
Thời gian: ~45 phút
Mã mẫu:

input_list = input("Nhập danh sách số (cách nhau bằng dấu cách): ").split()
numbers = [int(x) for x in input_list]
unique_numbers = list(set(numbers))
print(f"Danh sách sau khi loại bỏ trùng lặp: {unique_numbers}")

Công dụng thực tế:

  • Xử lý dữ liệu trong cơ sở dữ liệu để loại bỏ các bản ghi trùng (ví dụ: danh sách khách hàng không lặp lại).
  • Dùng trong phân tích dữ liệu để làm sạch tập dữ liệu trước khi phân tích (data cleaning).

Lời khuyên

  • Thực hành: Dành khoảng 30-60 phút cho mỗi bài, tổng cộng 4 giờ. Chạy thử với nhiều giá trị đầu vào khác nhau để kiểm tra tính đúng đắn.
  • Ứng dụng thêm: Nghĩ xem bạn có thể kết hợp các bài này vào một dự án lớn hơn không (ví dụ: một công cụ tính toán đa năng).
  • Kiểm tra lỗi: Thử nhập dữ liệu không hợp lệ để xem chương trình phản ứng thế nào và cải thiện nếu cần.

Ngày 4

Ngày 5: Cấu trúc dữ liệu trong Python (4 giờ học)

Lịch học gợi ý (4 giờ)

  • Giờ 1: Stack (30 phút) + Queue (30 phút).
  • Giờ 2: Dictionary (45 phút) + Tuple (15 phút).
  • Giờ 3: Cây (60 phút).
  • Giờ 4: Danh sách liên kết (45 phút) + Ôn tập (15 phút).

1. Stack (Ngăn xếp) - 30 phút

Khái niệm: Stack là cấu trúc dữ liệu "vào sau, ra trước" (LIFO - Last In, First Out). Giống như xếp đĩa, bạn chỉ lấy được đĩa trên cùng.

Triển khai trong Python: Dùng list với append() (đẩy vào) và pop() (lấy ra).

# Tạo stack
stack = []

# Đẩy phần tử vào
stack.append(1)
stack.append(2)
stack.append(3)
print("Stack sau khi thêm:", stack)  # [1, 2, 3]

# Lấy phần tử ra
top = stack.pop()
print("Phần tử lấy ra:", top)  # 3
print("Stack sau khi pop:", stack)  # [1, 2]

Ứng dụng: Kiểm tra ngoặc hợp lệ (e.g., (), {}), quay lại thao tác (undo).

Bài tập: Viết hàm kiểm tra chuỗi ngoặc như "()" hoặc "({[]})" có hợp lệ không.


2. Queue (Hàng đợi) - 30 phút

Khái niệm: Queue là "vào trước, ra trước" (FIFO - First In, First Out). Như xếp hàng mua vé.

Triển khai trong Python: Dùng collections.deque cho hiệu quả (thay vì list).

from collections import deque

# Tạo queue
queue = deque()

# Thêm phần tử
queue.append(1)
queue.append(2)
queue.append(3)
print("Queue sau khi thêm:", list(queue))  # [1, 2, 3]

# Lấy phần tử
front = queue.popleft()
print("Phần tử lấy ra:", front)  # 1
print("Queue sau khi pop:", list(queue))  # [2, 3]

Ứng dụng: Xử lý tác vụ theo thứ tự, hàng đợi in ấn.

Bài tập: Mô phỏng hàng đợi người mua vé, thêm 5 người, phục vụ 3 người.


3. Dictionary (Từ điển) - 45 phút

Khái niệm: Lưu dữ liệu dạng key-value (khóa-giá trị), truy cập nhanh qua key.

Triển khai trong Python:

# Tạo dictionary
my_dict = {"name": "Alex", "age": 25, "city": "Hanoi"}

# Truy cập
print(my_dict["name"])  # Alex

# Thêm/sửa
my_dict["job"] = "Developer"
my_dict["age"] = 26
print("Sau khi cập nhật:", my_dict)  # {'name': 'Alex', 'age': 26, 'city': 'Hanoi', 'job': 'Developer'}

# Xóa
del my_dict["city"]
print("Sau khi xóa:", my_dict)  # {'name': 'Alex', 'age': 26, 'job': 'Developer'}

Ứng dụng: Đếm tần suất từ, lưu thông tin cấu trúc (như hồ sơ).

Bài tập: Đếm số lần xuất hiện của mỗi chữ cái trong chuỗi "hello world".


4. Tuple (Bộ dữ liệu) - 30 phút

Khái niệm: Giống list nhưng không thay đổi được (immutable), dùng để lưu dữ liệu cố định.

Triển khai trong Python:

# Tạo tuple
my_tuple = (1, 2, 3, "hello")

# Truy cập
print(my_tuple[0])  # 1
print(my_tuple[-1])  # hello

# Đếm và tìm
print(my_tuple.count(2))  # 1 (số lần xuất hiện của 2)
print(my_tuple.index("hello"))  # 3 (vị trí của "hello")

Ứng dụng: Lưu tọa độ (x, y), trả về nhiều giá trị từ hàm.

Bài tập: Viết hàm trả về tuple chứa (tổng, hiệu) của 2 số.


Lưu ý

  • Thực hành: Sau mỗi phần, chạy code và thử sửa đổi (thêm/xóa phần tử, in ngược, v.v.).
  • Debug: Dùng print() để kiểm tra giá trị giữa chừng.
  • Mở rộng: Tìm hiểu thêm về set (tập hợp) hoặc cách tối ưu cây nhị phân.

📌 Tóm tắt

Cấu trúc dữ liệuĐặc điểm chính
StackLIFO (Vào sau, ra trước)
QueueFIFO (Vào trước, ra trước)
DictionaryDữ liệu dạng key-value, truy xuất nhanh
TupleGiống list nhưng không thay đổi được
TreeDạng phân cấp, có nút gốc và nút con
Linked ListDanh sách các nút liên kết với nhau

Ngày 6: Lập trình hướng đối tượng (OOP) trong Python

Tôi sẽ thiết kế một khóa học 5 giờ về Lập trình hướng đối tượng (OOP) trong Python, chia thành các phần rõ ràng, mỗi phần khoảng 1 giờ. Nội dung bao gồm lý thuyết cơ bản, ví dụ thực tế, và bài tập để bạn thực hành. Mục tiêu là bạn sẽ nắm được các khái niệm cốt lõi của OOP (class, object, inheritance, encapsulation, polymorphism) và áp dụng chúng trong Python.

Giờ 1: Giới thiệu và Class/Object cơ bản

Mục tiêu:

Hiểu class, object là gì và cách tạo chúng trong Python.

Lý thuyết (20 phút):

  • OOP là gì? Lập trình dựa trên "đối tượng" (object) mô phỏng thế giới thực.
  • Class: Bản thiết kế (blueprint) của đối tượng.
  • Object: Thực thể cụ thể được tạo từ class.
  • Cú pháp: class TenClass:.

Ví dụ thực hành (30 phút):

# Định nghĩa class
class Dog:
    def __init__(self, name, age):  # Hàm khởi tạo
        self.name = name  # Thuộc tính (attribute)
        self.age = age

    def bark(self):  # Phương thức (method)
        print(f"{self.name} says: Woof!")

# Tạo object
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)

# Truy cập thuộc tính và phương thức
print(dog1.name, dog1.age)  # Buddy 3
dog1.bark()  # Buddy says: Woof!
dog2.bark()  # Max says: Woof!

Bài tập (10 phút):

Tạo class Car với thuộc tính brand, speed và phương thức honk() in ra "Beep beep!".


Giờ 2: Encapsulation (Đóng gói)

Mục tiêu:

Học cách bảo vệ dữ liệu và ẩn chi tiết triển khai.

Lý thuyết (20 phút):

  • Encapsulation: Giới hạn truy cập trực tiếp vào dữ liệu, dùng thuộc tính private/public.
  • Trong Python: Dùng _ (protected) hoặc __ (private) trước tên thuộc tính.

Ví dụ thực hành (30 phút):

class Person:
    def __init__(self, name, age):
        self.name = name  # Public
        self.__age = age  # Private

    def get_age(self):  # Getter
        return self.__age

    def set_age(self, age):  # Setter
        if age > 0:
            self.__age = age
        else:
            print("Age must be positive!")

# Tạo object
person = Person("Alice", 25)
print(person.name)  # Alice
print(person.get_age())  # 25
person.set_age(30)
print(person.get_age())  # 30
person.set_age(-5)  # Age must be positive!

Bài tập (10 phút):

Tạo class BankAccount với thuộc tính private __balance, thêm phương thức deposit()withdraw() để thay đổi số dư (kiểm tra số dư không âm).


Giờ 3: Inheritance (Kế thừa)

Mục tiêu:

Hiểu cách tái sử dụng code qua kế thừa.

Lý thuyết (20 phút):

  • Inheritance: Class con (subclass) thừa hưởng thuộc tính/phương thức từ class cha (superclass).
  • Cú pháp: class SubClass(SuperClass):

Ví dụ thực hành (30 phút):

class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f"{self.name} is eating.")

class Cat(Animal):  # Kế thừa từ Animal
    def meow(self):
        print(f"{self.name} says: Meow!")

class Dog(Animal):
    def bark(self):
        print(f"{self.name} says: Woof!")

# Sử dụng
cat = Cat("Luna")
dog = Dog("Rex")
cat.eat()  # Luna is eating.
cat.meow()  # Luna says: Meow!
dog.eat()  # Rex is eating.
dog.bark()  # Rex says: Woof!

Bài tập (10 phút):

Tạo class Vehicle với thuộc tính speed, sau đó tạo subclass BicycleMotorcycle với phương thức riêng (e.g., ring_bell(), rev_engine()).


Giờ 4: Polymorphism (Đa hình)

Mục tiêu:

Học cách các đối tượng khác nhau phản hồi cùng một phương thức theo cách riêng.

Lý thuyết (20 phút):

  • Polymorphism: Nhiều class có thể dùng chung tên phương thức, nhưng hành vi khác nhau.

Ví dụ thực hành (30 phút):

class Animal:
    def speak(self):
        pass  # Phương thức trống, để subclass định nghĩa

class Cat(Animal):
    def speak(self):
        return "Meow!"

class Dog(Animal):
    def speak(self):
        return "Woof!"

# Đa hình
animals = [Cat(), Dog()]
for animal in animals:
    print(animal.speak())  # Meow! rồi Woof!

Bài tập (10 phút):

Tạo class Shape với phương thức area(), sau đó tạo subclass Circle (diện tích: πr²) và Rectangle (diện tích: dài × rộng). Gọi area() cho từng hình.


Giờ 5: Tổng hợp và Dự án nhỏ

Mục tiêu:

Kết hợp tất cả khái niệm vào một ví dụ thực tế.

Dự án: Quản lý thư viện (40 phút)

class Book:
    def __init__(self, title, author):
        self.__title = title
        self.__author = author
        self.__is_borrowed = False

    def get_title(self):
        return self.__title

    def borrow(self):
        if not self.__is_borrowed:
            self.__is_borrowed = True
            print(f"{self.__title} has been borrowed.")
        else:
            print(f"{self.__title} is already borrowed.")

class Library:
    def __init__(self):
        self.books = []

    def add_book(self, book):
        self.books.append(book)
        print(f"Added {book.get_title()} to library.")

# Sử dụng
book1 = Book("Python 101", "John Doe")
library = Library()
library.add_book(book1)
book1.borrow()

Lịch học gợi ý (5 giờ)

  • Giờ 1: Class/Object cơ bản.
  • Giờ 2: Encapsulation.
  • Giờ 3: Inheritance.
  • Giờ 4: Polymorphism.
  • Giờ 5: Dự án tổng hợp.

Trong 5 giờ, bạn có thể học về các khái niệm chính của OOP trong Python:

  1. Đối tượng (Object)
  2. Lớp (Class)
  3. Phương thức & Constructor (__init__)
  4. Kế thừa (Inheritance)

🏆 1. Đối tượng và Lớp (Object & Class)

Lớp (Class) là một bản thiết kế (blueprint) để tạo ra đối tượng (Object).

🔹 Ví dụ về Class và Object

class Nguoi:
    def __init__(self, ten, tuoi):
        self.ten = ten
        self.tuoi = tuoi

# Tạo đối tượng từ lớp
nguoi_1 = Nguoi("An", 25)
nguoi_2 = Nguoi("Bình", 30)

# Truy xuất dữ liệu
print(nguoi_1.ten, "-", nguoi_1.tuoi)  # An - 25
print(nguoi_2.ten, "-", nguoi_2.tuoi)  # Bình - 30

📌 Ghi nhớ:

  • class dùng để định nghĩa một lớp.
  • self đại diện cho đối tượng của lớp.

🔥 2. Phương thức (Method) & Constructor (__init__)

Phương thức là các hàm được định nghĩa bên trong lớp để xử lý dữ liệu.
Constructor (__init__) là phương thức đặc biệt được gọi khi tạo đối tượng.

🔹 Ví dụ về phương thức

class SinhVien:
    def __init__(self, ten, diem):
        self.ten = ten
        self.diem = diem

    def hien_thi(self):
        print(f"Sinh viên {self.ten} có điểm {self.diem}")

# Tạo đối tượng
sv = SinhVien("Linh", 9)

# Gọi phương thức
sv.hien_thi()  # Sinh viên Linh có điểm 9

📌 Ghi nhớ:

  • __init__() chạy tự động khi tạo đối tượng.
  • Phương thức truy cập dữ liệu bằng self.

🏗 3. Kế thừa (Inheritance)

Kế thừa giúp một lớp con (child class) sử dụng lại các thuộc tính và phương thức của lớp cha (parent class).

🔹 Ví dụ về kế thừa

class Nguoi:
    def __init__(self, ten, tuoi):
        self.ten = ten
        self.tuoi = tuoi

    def hien_thi(self):
        print(f"Tôi là {self.ten}, {self.tuoi} tuổi")

# Lớp SinhVien kế thừa từ Nguoi
class SinhVien(Nguoi):
    def __init__(self, ten, tuoi, diem):
        super().__init__(ten, tuoi)  # Gọi constructor của lớp cha
        self.diem = diem

    def hien_thi(self):
        super().hien_thi()
        print(f"Điểm trung bình: {self.diem}")

# Tạo đối tượng SinhVien
sv = SinhVien("Hoàng", 20, 8.5)
sv.hien_thi()

📌 Ghi nhớ:

  • super().__init__() gọi constructor của lớp cha.
  • Lớp con có thể ghi đè phương thức (hien_thi()).

🎯 Bài tập thực hành OOP

🔥 Bài 1: Tạo lớp HinhTron

Viết lớp HinhTron có:

  • Thuộc tính: ban_kinh
  • Phương thức: chu_vi()dien_tich()

👉 Gợi ý: Dùng công thức:

  • Chu vi = 2 * 3.14 * r
  • Diện tích = 3.14 * r * r

🔥 Bài 2: Tạo lớp DongVat và lớp con Meo

  • Lớp DongVat: có phương thức keu() in ra "Động vật kêu"
  • Lớp Meo kế thừa DongVat và ghi đè keu() để in "Meo meo"

👉 Gợi ý: Dùng super().keu() nếu muốn gọi phương thức lớp cha.


🔥 Constructor (__init__) trong Python

1. Constructor là gì?

Constructor là một phương thức đặc biệt trong lớp Python, được gọi tự động khi một đối tượng mới được tạo.

  • Trong Python, constructor được định nghĩa bằng __init__().
  • Nó dùng để khởi tạo giá trị ban đầu cho các thuộc tính của đối tượng.

2. Cách sử dụng __init__()

Cú pháp của constructor:

class TenLop:
    def __init__(self, tham_so1, tham_so2, ...):
        self.thuoc_tinh1 = tham_so1
        self.thuoc_tinh2 = tham_so2

📌 Ví dụ cơ bản:

class Nguoi:
    def __init__(self, ten, tuoi):
        self.ten = ten  # Gán giá trị tham số cho thuộc tính
        self.tuoi = tuoi

# Tạo đối tượng
nguoi1 = Nguoi("An", 25)

# In ra thuộc tính của đối tượng
print(nguoi1.ten)  # An
print(nguoi1.tuoi)  # 25

🚀 Khi tạo nguoi1 = Nguoi("An", 25), Python tự động gọi __init__()
self.ten = "An"self.tuoi = 25


3. Constructor mặc định (Không có tham số)

Nếu không cần tham số, constructor có thể viết như sau:

class DongVat:
    def __init__(self):
        print("Một con vật mới đã được tạo!")

# Tạo đối tượng
dv = DongVat()  # Output: Một con vật mới đã được tạo!

📌 Ghi nhớ: Nếu constructor không có tham số, nó vẫn được gọi tự động khi tạo đối tượng.


4. Constructor với giá trị mặc định

Có thể đặt giá trị mặc định cho tham số để tránh lỗi khi không truyền giá trị.

class HocSinh:
    def __init__(self, ten="Không tên", lop="12A1"):
        self.ten = ten
        self.lop = lop

# Tạo đối tượng không truyền tham số
hs1 = HocSinh()
print(hs1.ten, "-", hs1.lop)  # Không tên - 12A1

# Tạo đối tượng có truyền tham số
hs2 = HocSinh("Minh", "12B3")
print(hs2.ten, "-", hs2.lop)  # Minh - 12B3

📌 Lợi ích: Nếu người dùng không truyền tham số, chương trình vẫn chạy bình thường.


5. Constructor trong kế thừa (super())

Khi một lớp kế thừa từ lớp khác, ta có thể dùng super().__init__() để gọi constructor của lớp cha.

📌 Ví dụ:

class Nguoi:
    def __init__(self, ten, tuoi):
        self.ten = ten
        self.tuoi = tuoi

# Lớp SinhVien kế thừa từ Nguoi
class SinhVien(Nguoi):
    def __init__(self, ten, tuoi, diem):
        super().__init__(ten, tuoi)  # Gọi constructor của lớp cha
        self.diem = diem

# Tạo đối tượng
sv = SinhVien("Hoa", 20, 8.5)

print(sv.ten)  # Hoa
print(sv.tuoi)  # 20
print(sv.diem)  # 8.5

🚀 super().__init__(ten, tuoi) giúp SinhVien kế thừa thuộc tính từ lớp Nguoi.


6. Constructor có thể làm gì?

Constructor không chỉ dùng để khởi tạo biến mà còn có thể:
✅ Kiểm tra dữ liệu đầu vào
✅ Mở file, kết nối cơ sở dữ liệu
✅ Thiết lập trạng thái ban đầu của đối tượng

📌 Ví dụ kiểm tra dữ liệu hợp lệ:

class NhanVien:
    def __init__(self, ten, luong):
        if luong < 0:
            raise ValueError("Lương không thể âm!")
        self.ten = ten
        self.luong = luong

# Tạo đối tượng hợp lệ
nv = NhanVien("Nam", 5000)
print(nv.ten, "-", nv.luong)  # Nam - 5000

# Tạo đối tượng với lương âm (sẽ báo lỗi)
# nv2 = NhanVien("Linh", -3000)  # ❌ ValueError: Lương không thể âm!

🎯 Tóm tắt

Tính năngConstructor (__init__())
Tự động gọiKhi tạo đối tượng từ lớp
Khởi tạo giá trịGán giá trị ban đầu cho thuộc tính
Có thể có tham sốDùng để truyền dữ liệu vào đối tượng
Có thể có giá trị mặc địnhTránh lỗi khi không truyền tham số
Dùng trong kế thừasuper().__init__() để gọi constructor lớp cha

🔥 Ví dụ thực tế về Constructor (__init__()) trong Python

Dưới đây là một số tình huống thực tế về cách sử dụng constructor (__init__()) trong Python.


1. Tạo hệ thống quản lý sinh viên

📌 Yêu cầu:

  • Lớp SinhVien có các thuộc tính: ho_ten, tuoi, diem.
  • Khi tạo sinh viên mới, nếu diem < 0 hoặc diem > 10, sẽ báo lỗi.

📝 Cách làm:

class SinhVien:
    def __init__(self, ho_ten, tuoi, diem):
        if diem < 0 or diem > 10:
            raise ValueError("Điểm phải từ 0 đến 10!")
        self.ho_ten = ho_ten
        self.tuoi = tuoi
        self.diem = diem

    def hien_thi(self):
        print(f"Sinh viên: {self.ho_ten}, Tuổi: {self.tuoi}, Điểm: {self.diem}")

# Tạo sinh viên hợp lệ
sv1 = SinhVien("Nguyễn Văn A", 20, 8.5)
sv1.hien_thi()  # Output: Sinh viên: Nguyễn Văn A, Tuổi: 20, Điểm: 8.5

# Tạo sinh viên có điểm không hợp lệ (sẽ báo lỗi)
# sv2 = SinhVien("Trần Thị B", 22, 12)  # ❌ ValueError: Điểm phải từ 0 đến 10!

🚀 Lợi ích: Giúp kiểm tra dữ liệu hợp lệ ngay từ khi khởi tạo đối tượng.


2. Xây dựng hệ thống tài khoản ngân hàng

📌 Yêu cầu:

  • Lớp TaiKhoanNganHang có:
    • so_tai_khoan
    • chu_tai_khoan
    • so_du (ban đầu mặc định là 0)
  • Tự động hiển thị thông tin khi tạo tài khoản.

📝 Cách làm:

class TaiKhoanNganHang:
    def __init__(self, so_tai_khoan, chu_tai_khoan, so_du=0):
        self.so_tai_khoan = so_tai_khoan
        self.chu_tai_khoan = chu_tai_khoan
        self.so_du = so_du

        print(f"Tài khoản {self.so_tai_khoan} của {self.chu_tai_khoan} đã được tạo với số dư: {self.so_du} VNĐ")

# Tạo tài khoản mới
tk1 = TaiKhoanNganHang("123456789", "Nguyễn Văn C", 500000)
tk2 = TaiKhoanNganHang("987654321", "Lê Thị D")  # Số dư mặc định 0

🚀 Lợi ích:

  • Mặc định số dư = 0 nếu không nhập.
  • Hiển thị thông báo ngay khi tạo tài khoản.

3. Quản lý sách trong thư viện

📌 Yêu cầu:

  • Lớp Sach có các thuộc tính: tieu_de, tac_gia, nam_xuat_ban.
  • Nếu năm xuất bản lớn hơn năm hiện tại, báo lỗi.

📝 Cách làm:

from datetime import datetime

class Sach:
    def __init__(self, tieu_de, tac_gia, nam_xuat_ban):
        nam_hien_tai = datetime.now().year
        if nam_xuat_ban > nam_hien_tai:
            raise ValueError("Năm xuất bản không hợp lệ!")
        self.tieu_de = tieu_de
        self.tac_gia = tac_gia
        self.nam_xuat_ban = nam_xuat_ban

    def hien_thi(self):
        print(f"Sách: {self.tieu_de}, Tác giả: {self.tac_gia}, Năm XB: {self.nam_xuat_ban}")

# Tạo sách hợp lệ
sach1 = Sach("Doraemon", "Fujiko F. Fujio", 1990)
sach1.hien_thi()  # Output: Sách: Doraemon, Tác giả: Fujiko F. Fujio, Năm XB: 1990

# Tạo sách với năm xuất bản không hợp lệ (sẽ báo lỗi)
# sach2 = Sach("Tương lai AI", "John Doe", 2030)  # ❌ ValueError: Năm xuất bản không hợp lệ!

🚀 Lợi ích:

  • Kiểm tra hợp lệ ngay khi tạo đối tượng.

4. Quản lý nhân viên công ty

📌 Yêu cầu:

  • Lớp NhanVien có các thuộc tính: ho_ten, luong_co_ban, thuong.
  • Lương thực nhận = luong_co_ban + thuong.
  • Nếu luong_co_ban nhỏ hơn 0, báo lỗi.

📝 Cách làm:

class NhanVien:
    def __init__(self, ho_ten, luong_co_ban, thuong=0):
        if luong_co_ban < 0:
            raise ValueError("Lương cơ bản không thể âm!")
        self.ho_ten = ho_ten
        self.luong_co_ban = luong_co_ban
        self.thuong = thuong
        self.luong_thuc_nhan = self.luong_co_ban + self.thuong

    def hien_thi(self):
        print(f"Nhân viên: {self.ho_ten}, Lương thực nhận: {self.luong_thuc_nhan} VNĐ")

# Tạo nhân viên hợp lệ
nv1 = NhanVien("Trần Văn E", 7000000, 1000000)
nv1.hien_thi()  # Output: Nhân viên: Trần Văn E, Lương thực nhận: 8000000 VNĐ

# Tạo nhân viên với lương âm (sẽ báo lỗi)
# nv2 = NhanVien("Lê Văn F", -5000000)  # ❌ ValueError: Lương cơ bản không thể âm!

🚀 Lợi ích:

  • Kiểm tra lỗi trước khi thêm nhân viên.
  • Tự động tính lương thực nhận.

🎯 Tóm tắt

Ví dụConstructor làm gì?
Quản lý sinh viênKiểm tra điểm hợp lệ (0-10)
Tài khoản ngân hàngTự động tạo tài khoản & đặt số dư mặc định
Quản lý sáchKiểm tra năm xuất bản không vượt quá năm hiện tại
Quản lý nhân viênKiểm tra lương hợp lệ & tính lương thực nhận

🔥 Ví dụ về Ví Ethereum (ETH) sử dụng Constructor (__init__()) trong Python

📌 Yêu cầu:

  1. Tạo lớp EthereumWallet để quản lý ví ETH.
  2. Khi tạo ví mới, tự động:
    • Tạo địa chỉ ví (giả lập bằng chuỗi hex).
    • Thiết lập số dư mặc định là 0 ETH.
  3. Cung cấp các phương thức để:
    • Kiểm tra số dư.
    • Nạp ETH vào ví.
    • Gửi ETH sang ví khác (phải kiểm tra số dư hợp lệ).

📝 Cách làm:

import random
import string

class EthereumWallet:
    def __init__(self, owner_name):
        self.owner_name = owner_name
        self.address = self.generate_address()
        self.balance = 0.0  # Số dư mặc định là 0 ETH

    def generate_address(self):
        """Tạo địa chỉ ví ETH ngẫu nhiên (giả lập)"""
        return "0x" + ''.join(random.choices(string.hexdigits, k=40)).lower()

    def check_balance(self):
        """Kiểm tra số dư ví"""
        print(f"Ví của {self.owner_name} ({self.address}) có số dư: {self.balance} ETH")

    def deposit(self, amount):
        """Nạp ETH vào ví"""
        if amount > 0:
            self.balance += amount
            print(f"Nạp {amount} ETH thành công! Số dư mới: {self.balance} ETH")
        else:
            print("❌ Số tiền nạp phải lớn hơn 0!")

    def send_eth(self, recipient_wallet, amount):
        """Gửi ETH sang ví khác"""
        if amount > self.balance:
            print("❌ Giao dịch thất bại: Số dư không đủ!")
        elif amount <= 0:
            print("❌ Số tiền gửi phải lớn hơn 0!")
        else:
            self.balance -= amount
            recipient_wallet.balance += amount
            print(f"✅ Gửi {amount} ETH thành công đến {recipient_wallet.address}")
            print(f"Số dư còn lại trong ví: {self.balance} ETH")

# Tạo 2 ví ETH
alice_wallet = EthereumWallet("Alice")
bob_wallet = EthereumWallet("Bob")

# Kiểm tra số dư ban đầu
alice_wallet.check_balance()
bob_wallet.check_balance()

# Alice nạp 10 ETH vào ví
alice_wallet.deposit(10)

# Alice gửi 3 ETH cho Bob
alice_wallet.send_eth(bob_wallet, 3)

# Kiểm tra số dư sau giao dịch
alice_wallet.check_balance()
bob_wallet.check_balance()

Giải thích mã nguồn:

  1. __init__()
    • Khi tạo một ví mới, nó sẽ tự động tạo địa chỉ ETH ngẫu nhiên và thiết lập số dư về 0.0.
  2. Phương thức generate_address()
    • Giả lập một địa chỉ ETH bằng cách tạo một chuỗi hex ngẫu nhiên dài 40 ký tự.
  3. Phương thức check_balance()
    • Hiển thị số dư hiện tại của ví.
  4. Phương thức deposit(amount)
    • Nạp ETH vào ví (chỉ chấp nhận số dương).
  5. Phương thức send_eth(recipient_wallet, amount)
    • Kiểm tra số dư trước khi gửi ETH đến ví khác.
    • Nếu số dư hợp lệ, tiến hành giao dịch và cập nhật số dư của cả hai ví.

🎯 Kết quả mong đợi:

Ví của Alice (0x4b9f0d3a19f72e85a4...) có số dư: 0.0 ETH
Ví của Bob (0x2e8a1c4d99b62f1a7b...) có số dư: 0.0 ETH

Nạp 10 ETH thành công! Số dư mới: 10.0 ETH

✅ Gửi 3 ETH thành công đến 0x2e8a1c4d99b62f1a7b...
Số dư còn lại trong ví: 7.0 ETH

Ví của Alice (0x4b9f0d3a19f72e85a4...) có số dư: 7.0 ETH
Ví của Bob (0x2e8a1c4d99b62f1a7b...) có số dư: 3.0 ETH

🚀 Mở rộng:

  • Thêm xác thực giao dịch bằng Private Key
  • Kết nối API để gửi ETH thực tế qua blockchain
  • Ghi lịch sử giao dịch vào file hoặc database

🔥 Mở rộng ví Ethereum: Xác thực giao dịch & Lịch sử giao dịch

Chúng ta sẽ thêm hai tính năng quan trọng:
Xác thực giao dịch bằng Private Key (Giả lập ký giao dịch)
Lưu lịch sử giao dịch vào danh sách


🏗 1. Mô tả nâng cấp

  1. Mỗi ví sẽ có một Private Key để xác thực giao dịch.
  2. Giao dịch chỉ được thực hiện nếu có chữ ký hợp lệ.
  3. Lịch sử giao dịch được lưu lại để tra cứu.

📝 Cập nhật mã nguồn

import random
import string
import hashlib

class EthereumWallet:
    def __init__(self, owner_name):
        self.owner_name = owner_name
        self.address = self.generate_address()
        self.private_key = self.generate_private_key()
        self.balance = 0.0
        self.transaction_history = []  # Lưu lịch sử giao dịch

    def generate_address(self):
        """Tạo địa chỉ ví ETH giả lập"""
        return "0x" + ''.join(random.choices(string.hexdigits, k=40)).lower()

    def generate_private_key(self):
        """Tạo Private Key ngẫu nhiên"""
        return ''.join(random.choices(string.hexdigits, k=64)).lower()

    def check_balance(self):
        """Kiểm tra số dư ví"""
        print(f"Ví {self.owner_name} ({self.address}) có số dư: {self.balance} ETH")

    def deposit(self, amount):
        """Nạp ETH vào ví"""
        if amount > 0:
            self.balance += amount
            self.transaction_history.append(f"Nạp {amount} ETH")
            print(f"✅ Nạp {amount} ETH thành công! Số dư mới: {self.balance} ETH")
        else:
            print("❌ Số tiền nạp phải lớn hơn 0!")

    def sign_transaction(self, recipient, amount):
        """Giả lập ký giao dịch bằng private key (hash SHA256)"""
        message = f"{self.address}->{recipient.address}:{amount}:{self.private_key}"
        return hashlib.sha256(message.encode()).hexdigest()

    def send_eth(self, recipient_wallet, amount, signature):
        """Gửi ETH với xác thực chữ ký"""
        if amount > self.balance:
            print("❌ Giao dịch thất bại: Số dư không đủ!")
        elif amount <= 0:
            print("❌ Số tiền gửi phải lớn hơn 0!")
        elif signature != self.sign_transaction(recipient_wallet, amount):
            print("❌ Giao dịch bị từ chối: Chữ ký không hợp lệ!")
        else:
            self.balance -= amount
            recipient_wallet.balance += amount
            self.transaction_history.append(f"Gửi {amount} ETH đến {recipient_wallet.address}")
            recipient_wallet.transaction_history.append(f"Nhận {amount} ETH từ {self.address}")
            print(f"✅ Giao dịch thành công! Gửi {amount} ETH đến {recipient_wallet.address}")

    def show_transaction_history(self):
        """Hiển thị lịch sử giao dịch"""
        print(f"\n📜 Lịch sử giao dịch của {self.owner_name}:")
        for tx in self.transaction_history:
            print(f"- {tx}")

# Tạo 2 ví ETH
alice_wallet = EthereumWallet("Alice")
bob_wallet = EthereumWallet("Bob")

# Kiểm tra số dư ban đầu
alice_wallet.check_balance()
bob_wallet.check_balance()

# Alice nạp 10 ETH vào ví
alice_wallet.deposit(10)

# Alice tạo chữ ký và gửi 3 ETH cho Bob
signature = alice_wallet.sign_transaction(bob_wallet, 3)
alice_wallet.send_eth(bob_wallet, 3, signature)

# Kiểm tra số dư sau giao dịch
alice_wallet.check_balance()
bob_wallet.check_balance()

# Xem lịch sử giao dịch
alice_wallet.show_transaction_history()
bob_wallet.show_transaction_history()

🎯 Cách hoạt động của nâng cấp

  1. Tạo ví: Mỗi ví có một Private Key và địa chỉ ETH.
  2. Nạp ETH: ETH có thể được nạp vào ví, lưu vào lịch sử giao dịch.
  3. Xác thực giao dịch:
    • Khi gửi ETH, hệ thống tạo một chữ ký dựa trên Private Key.
    • Nếu chữ ký không khớp, giao dịch bị từ chối.
  4. Lưu lịch sử giao dịch: Mọi giao dịch được ghi lại để có thể kiểm tra sau.

🏆 Kết quả mong đợi

Ví Alice (0x4b9f0d3a19f72e85a4...) có số dư: 0.0 ETH
Ví Bob (0x2e8a1c4d99b62f1a7b...) có số dư: 0.0 ETH

✅ Nạp 10 ETH thành công! Số dư mới: 10.0 ETH
✅ Giao dịch thành công! Gửi 3 ETH đến 0x2e8a1c4d99b62f1a7b...

Ví Alice (0x4b9f0d3a19f72e85a4...) có số dư: 7.0 ETH
Ví Bob (0x2e8a1c4d99b62f1a7b...) có số dư: 3.0 ETH

📜 Lịch sử giao dịch của Alice:
- Nạp 10 ETH
- Gửi 3 ETH đến 0x2e8a1c4d99b62f1a7b...

📜 Lịch sử giao dịch của Bob:
- Nhận 3 ETH từ 0x4b9f0d3a19f72e85a4...

🚀 Mở rộng thêm

  • Thêm phí giao dịch (giữ lại một phần ETH khi gửi).
  • Kết nối API Blockchain (Infura, Alchemy) để giao dịch thực tế.
  • Tích hợp mã QR để quét và gửi ETH nhanh hơn.

🌐 Kết nối API Blockchain để gửi ETH thực tế

Chúng ta sẽ sử dụng Web3.py để kết nối với mạng Ethereum thông qua Infura hoặc Alchemy, giúp gửi ETH thực tế.


1. Chuẩn bị trước khi lập trình

📌 Cài đặt thư viện Web3.py:

pip install web3

📌 Đăng ký tài khoản trên Infura hoặc Alchemy:

📌 Tạo ví Ethereum trên Metamask & lấy thông tin quan trọng:

  • Private Key của ví gửi ETH.
  • Địa chỉ ví nhận ETH.

⚠️ Lưu ý bảo mật: Không chia sẻ Private Key với bất kỳ ai!


🚀 2. Code gửi ETH thực tế với Web3.py

📌 Gửi ETH từ một ví đến một ví khác

from web3 import Web3

# Thay thế bằng API Endpoint từ Infura hoặc Alchemy
INFURA_URL = "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID"

# Kết nối với mạng Ethereum
web3 = Web3(Web3.HTTPProvider(INFURA_URL))

# Kiểm tra kết nối thành công
if web3.is_connected():
    print("✅ Đã kết nối với Ethereum Blockchain!")
else:
    print("❌ Kết nối thất bại!")

# Thông tin giao dịch
SENDER_ADDRESS = "0xYOUR_SENDER_WALLET_ADDRESS"  # Ví gửi
PRIVATE_KEY = "YOUR_PRIVATE_KEY"  # ⚠️ Không chia sẻ với ai!
RECEIVER_ADDRESS = "0xYOUR_RECEIVER_WALLET_ADDRESS"  # Ví nhận
AMOUNT_ETH = 0.01  # Số ETH muốn gửi

# Lấy nonce của giao dịch (số lượng giao dịch đã gửi từ ví này)
nonce = web3.eth.get_transaction_count(SENDER_ADDRESS)

# Tạo giao dịch
tx = {
    'nonce': nonce,
    'to': RECEIVER_ADDRESS,
    'value': web3.to_wei(AMOUNT_ETH, 'ether'),  # Chuyển ETH thành Wei (đơn vị nhỏ nhất)
    'gas': 21000,  # Gas limit tiêu chuẩn cho giao dịch ETH
    'gasPrice': web3.to_wei(20, 'gwei'),  # Phí gas (Gwei)
    'chainId': 11155111,  # ID mạng Sepolia Testnet (Mainnet là 1)
}

# Ký giao dịch bằng Private Key
signed_tx = web3.eth.account.sign_transaction(tx, PRIVATE_KEY)

# Gửi giao dịch lên blockchain
tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction)

# Lấy ID giao dịch (Transaction Hash)
print(f"✅ Giao dịch đang xử lý: {web3.to_hex(tx_hash)}")

# Kiểm tra trạng thái giao dịch
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
if receipt.status == 1:
    print("✅ Giao dịch thành công!")
else:
    print("❌ Giao dịch thất bại!")

🎯 Giải thích mã nguồn

  1. Kết nối với Ethereum bằng Infura
    • Sử dụng Web3.HTTPProvider(INFURA_URL) để kết nối với mạng blockchain.
  2. Tạo & ký giao dịch
    • Giao dịch chứa to (địa chỉ nhận), value (số ETH), gas (phí).
    • Sử dụng web3.eth.account.sign_transaction() để ký bằng Private Key.
  3. Gửi giao dịch lên blockchain
    • web3.eth.send_raw_transaction() để gửi ETH.
    • Chờ xác nhận bằng wait_for_transaction_receipt().

🔬 Kiểm tra giao dịch trên Blockchain

Sau khi gửi, lấy Transaction Hash và kiểm tra trên Etherscan:
🔗 Ethereum Mainnet: https://etherscan.io/
🔗 Sepolia Testnet: https://sepolia.etherscan.io/

Dán Transaction Hash vào ô tìm kiếm để xem trạng thái giao dịch.


⚠️ Lưu ý quan trọng

  • Không bao giờ chia sẻ Private Key.
  • Nếu test, hãy dùng ví Testnet (Sepolia, Goerli) thay vì Mainnet.
  • Phí gas có thể thay đổi, kiểm tra trên Ethereum Gas Tracker.

🚀 Mở rộng thêm

  • Tạo Smart Contract để gửi ETH tự động.
  • Tích hợp Metamask để gửi ETH từ trình duyệt.
  • Xây dựng giao diện web với Flask/Django.

Ngày 7

Bước 2: Bắt đầu phát triển phần mềm (Xây dựng dự án)

Virtual Environment trong Python là gì?

Virtual Environment (môi trường ảo) trong Python là một cách để tạo ra một môi trường độc lập cho từng dự án, giúp tránh xung đột giữa các phiên bản thư viện. Khi sử dụng Virtual Environment, bạn có thể cài đặt các gói Python riêng cho từng dự án mà không ảnh hưởng đến hệ thống hoặc các dự án khác.


Cách sử dụng Virtual Environment trên LinuxWindows

1. Cài đặt Virtual Environment

Trước tiên, bạn cần cài đặt module venv (được tích hợp sẵn từ Python 3.3 trở đi). Nếu bạn chưa có, hãy cài đặt bằng lệnh:

pip install virtualenv

2. Tạo Virtual Environment

Di chuyển đến thư mục dự án của bạn và chạy lệnh:

Trên Linux/macOS

python3 -m venv myenv

Trên Windows

python -m venv myenv

Lệnh trên sẽ tạo một thư mục myenv chứa môi trường ảo.


3. Kích hoạt Virtual Environment

Trên Linux/macOS

source myenv/bin/activate
python3 -m venv ~/py_envs
source ~/py_envs/bin/activate
python3 -m pip install xyz

Trên Windows (CMD)

myenv\Scripts\activate

Trên Windows (PowerShell)

myenv\Scripts\Activate.ps1

Sau khi kích hoạt, bạn sẽ thấy tên môi trường (myenv) xuất hiện trước dấu nhắc lệnh.


4. Cài đặt gói trong Virtual Environment

Sau khi kích hoạt môi trường ảo, bạn có thể cài đặt các thư viện Python mà không ảnh hưởng đến hệ thống:

pip install <tên_thư_viện>

Ví dụ:

pip install numpy pandas

5. Kiểm tra danh sách thư viện đã cài đặt

pip list

6. Hủy kích hoạt Virtual Environment

Để thoát khỏi môi trường ảo, dùng lệnh:

deactivate

7. Xóa Virtual Environment

Nếu bạn muốn xóa môi trường ảo, chỉ cần xóa thư mục chứa nó:

rm -rf myenv   # Linux/macOS
rd /s /q myenv  # Windows (CMD)

8. Lưu danh sách thư viện và cài đặt lại

Nếu bạn muốn chia sẻ danh sách thư viện hoặc cài lại môi trường, hãy dùng:

Xuất danh sách thư viện

pip freeze > requirements.txt

Cài đặt lại từ danh sách

pip install -r requirements.txt

Tóm lại, Virtual Environment giúp cô lập thư viện cho từng dự án Python, tránh xung đột giữa các phiên bản. Việc sử dụng nó rất đơn giản với các lệnh venv, activate, deactivate, và pip.

Ví dụ

Ba lệnh sau được sử dụng để tạo và làm việc với một môi trường ảo trong Python:

python3 -m venv ~/py_envs
source ~/py_envs/bin/activate
python3 -m pip install xyz

Giải thích từng lệnh:

1. Tạo môi trường ảo

python3 -m venv ~/py_envs

🔹 Ý nghĩa:

  • python3 -m venv → Sử dụng mô-đun venv của Python để tạo một môi trường ảo.
  • ~/py_envs → Thư mục nơi môi trường ảo sẽ được tạo (nằm trong thư mục Home của user).

📌 Kết quả:

  • Một thư mục py_envs sẽ được tạo trong ~/, chứa các file cần thiết để chạy môi trường ảo:
    • bin/ (hoặc Scripts/ trên Windows) – Chứa các file thực thi.
    • lib/ – Chứa các thư viện Python cài đặt trong môi trường ảo.
    • include/ – Chứa các file header.
    • pyvenv.cfg – File cấu hình của môi trường ảo.

2. Kích hoạt môi trường ảo

source ~/py_envs/bin/activate

🔹 Ý nghĩa:

  • source → Chạy script để thiết lập môi trường hiện tại.
  • ~/py_envs/bin/activate → Chạy script kích hoạt môi trường ảo.

📌 Kết quả:

  • Dấu nhắc lệnh (terminal) sẽ thay đổi, hiển thị tên môi trường ảo, ví dụ:
    (py_envs) user@hostname:~$
    
  • Bây giờ, Python và pip sẽ sử dụng môi trường ảo thay vì hệ thống Python toàn cục.

3. Cài đặt một thư viện vào môi trường ảo

python3 -m pip install xyz

🔹 Ý nghĩa:

  • python3 -m pip → Chạy pip bên trong môi trường ảo.
  • install xyz → Cài đặt thư viện xyz vào môi trường ảo.

📌 Kết quả:

  • Thư viện xyz sẽ được cài đặt chỉ trong môi trường ảo (~/py_envs/lib/...), không ảnh hưởng đến hệ thống.
  • Bạn có thể kiểm tra các gói đã cài bằng:
    pip list
    

Tóm tắt:

  1. Tạo môi trường ảo trong thư mục ~/py_envs.
  2. Kích hoạt môi trường ảo để làm việc với nó.
  3. Cài đặt thư viện vào môi trường ảo mà không ảnh hưởng đến hệ thống.

Ngày 1

Ngày 2

Ngày 3

Bước 3: Bắt đầu phát triển phần mềm

Ngày 1

Ngày 2

Ngày 3

Ngày 4

Ngày 5

Ngày 6

Debugging trong Python bằng PyCharm IDE

Mục tiêu

Sau bài viết này bạn sẽ biết được cách:

- Thiết lập và sử dụng breakpoint trong PyCharm.
- Chạy chương trình ở chế độ Debug và kiểm tra giá trị biến.
- Sử dụng các công cụ như Step Over, Watch, và Evaluate Expression.
- Sửa lỗi chỉ số vượt quá giới hạn trong vòng lặp.

Dưới đây là một ví dụ minh họa các cách gỡ lỗi (debugging) thông thường trong Python bằng PyCharm IDE. Ví dụ này sẽ sử dụng một đoạn code đơn giản có lỗi, và tôi sẽ hướng dẫn từng bước cách sử dụng các công cụ gỡ lỗi trong PyCharm để tìm và sửa lỗi.

Ví dụ: Tính tổng các số trong danh sách

Mã nguồn (có lỗi cố ý)

def calculate_sum(numbers):
    total = 0
    for i in range(len(numbers) + 1):  # Lỗi 1: Vượt quá chỉ số của danh sách
        total += numbers[i]
    return total

def main():
    my_list = [1, 2, 3, 4, 5]
    result = calculate_sum(my_list)
    print(f"Tổng của danh sách là: {result}")

if __name__ == "__main__":
    main()

Vấn đề: Chương trình trên sẽ gặp lỗi IndexError: list index out of range vì vòng lặp for truy cập chỉ số vượt quá độ dài của danh sách.

Các bước gỡ lỗi trong PyCharm

Bước 1: Chạy chương trình và xác định lỗi

  1. Mở PyCharm, tạo một file Python mới (ví dụ: debug_example.py) và sao chép mã nguồn trên.
  2. Nhấn Run (Shift + F10) để chạy chương trình.
  3. PyCharm sẽ hiển thị lỗi trong cửa sổ Run:
    IndexError: list index out of range
    
    Điều này cho biết lỗi xảy ra trong hàm calculate_sum khi truy cập numbers[i].

Bước 2: Thiết lập điểm ngắt (Breakpoint)

  • Trong PyCharm, nhấp vào lề trái của dòng code mà bạn nghi ngờ có lỗi (ví dụ: dòng total += numbers[i] trong hàm calculate_sum).
  • Một dấu chấm đỏ sẽ xuất hiện, biểu thị breakpoint.
  • Điều này sẽ tạm dừng chương trình tại dòng đó khi chạy ở chế độ gỡ lỗi.

Bước 3: Chạy ở chế độ gỡ lỗi (Debug Mode)

  • Nhấn Debug (Shift + F9) hoặc nhấp vào biểu tượng con bọ (bug) trong thanh công cụ.
  • PyCharm sẽ chạy chương trình và dừng lại tại breakpoint đã đặt.
  • Cửa sổ Debug sẽ mở ra, hiển thị:
    • Variables: Các biến hiện tại (total, numbers, i).
    • Call Stack: Ngăn xếp lệnh gọi (cho biết chương trình đang ở đâu).

Bước 4: Kiểm tra giá trị biến

  • Tại breakpoint, kiểm tra giá trị của ilen(numbers) trong cửa sổ Variables.
  • Giả sử i = 5len(numbers) = 5, điều này cho thấy i đang truy cập chỉ số không hợp lệ (vì danh sách chỉ có chỉ số từ 0 đến 4).
  • Điều này xác nhận lỗi nằm ở giới hạn của vòng lặp for.

Bước 5: Sử dụng Watch để theo dõi biểu thức

  • Trong cửa sổ Debug, nhấp vào Add to Watches và nhập biểu thức len(numbers) hoặc i < len(numbers).
  • Điều này giúp bạn theo dõi các giá trị hoặc điều kiện trong suốt quá trình thực thi.

Bước 6: Bước qua code (Step Through Code)

Sử dụng các nút điều khiển trong cửa sổ Debug:

  • Step Over (F8): Chạy dòng hiện tại và chuyển đến dòng tiếp theo.
  • Step Into (F7): Đi vào bên trong hàm nếu có lời gọi hàm.
  • Resume Program (F9): Tiếp tục chạy cho đến breakpoint tiếp theo hoặc kết thúc.

Nhấn Step Over để kiểm tra từng lần lặp của vòng lặp. Khi i = 5, bạn sẽ thấy lỗi xảy ra.

Bước 7: Sửa lỗi

Dựa trên phân tích, lỗi xảy ra do vòng lặp chạy từ 0 đến len(numbers) (thay vì len(numbers) - 1). Sửa lại hàm calculate_sum:

def calculate_sum(numbers):
    total = 0
    for i in range(len(numbers)):  # Sửa lỗi: Bỏ +1
        total += numbers[i]
    return total

Bước 8: Kiểm tra lại

  • Chạy lại chương trình bằng Run hoặc Debug để xác nhận lỗi đã được sửa.
  • Kết quả mong đợi:
    Tổng của danh sách là: 15
    

Bước 9: Sử dụng công cụ bổ sung trong PyCharm

  • Evaluate Expression: Nhấp chuột phải tại breakpoint, chọn Evaluate Expression để kiểm tra giá trị của bất kỳ biểu thức nào (ví dụ: numbers[i]).
  • Conditional Breakpoint: Nhấp chuột phải vào breakpoint, thêm điều kiện (ví dụ: i >= len(numbers)) để chỉ dừng khi điều kiện đúng.
  • Log Breakpoint: Cấu hình breakpoint để in giá trị biến ra console mà không dừng chương trình.

Các mẹo gỡ lỗi khác trong PyCharm

  • Sử dụng Console để kiểm tra nhanh: Trong cửa sổ Python Console, bạn có thể nhập các lệnh để kiểm tra hàm hoặc biến, ví dụ:

    numbers = [1, 2, 3, 4, 5]
    len(numbers)
    
  • Kiểm tra Stack Trace: Khi lỗi xảy ra, nhấp vào các liên kết trong stack trace trong cửa sổ Run để nhảy đến dòng code gây lỗi.

  • Tìm kiếm lỗi phổ biến: Sử dụng tính năng Code Inspection của PyCharm (màu vàng/xanh dưới code) để phát hiện lỗi tiềm ẩn trước khi chạy.

RetroDash

RetroDash/
├── main.py                 # Điểm khởi chạy chính của bot
├── config/
│   ├── config.yaml        # Cấu hình (API keys, node URLs, v.v.)
│   └── settings.py        # Cài đặt mặc định
├── wallets/
│   ├── wallet_manager.py  # Quản lý ví chung (tạo, lưu trữ, truy xuất)
│   └── key_manager.py     # Quản lý khóa (mã hóa, giải mã)
├── tools/                 # Thư mục công cụ chung ở cấp gốc
│   ├── evm_wallet_gen.py  # Tạo ví cho EVM
│   ├── sol_wallet_gen.py  # Tạo ví cho Solana
│   ├── utils.py           # Hàm tiện ích chung (dùng cho tất cả blockchain)
│   └── __init__.py        # Khởi tạo module
├── evm/                   # Nhóm blockchain tương thích EVM
│   ├── core/
│   │   ├── blockchain.py  # Logic chung cho EVM (gửi tx, kiểm tra số dư)
│   │   ├── transaction.py # Xử lý giao dịch EVM
│   │   └── __init__.py    # Khởi tạo module
│   ├── adapters/
│   │   ├── ethereum.py    # Adapter cho Ethereum
│   │   ├── bsc.py         # Adapter cho Binance Smart Chain
│   │   ├── polygon.py     # Adapter cho Polygon
│   │   └── __init__.py    # Khởi tạo module
│   └── __init__.py        # Khởi tạo module
├── solana/
│   ├── core/
│   │   ├── blockchain.py  # Logic riêng cho Solana
│   │   ├── transaction.py # Xử lý giao dịch Solana
│   │   └── __init__.py    # Khởi tạo module
│   ├── adapter.py         # Adapter cho Solana
│   └── __init__.py        # Khởi tạo module
├── gui/                   # Thư mục cho GUI (tương lai)
│   ├── __init__.py        # Khởi tạo module
│   ├── main_window.py     # Cửa sổ chính
│   └── widgets.py         # Thành phần giao diện
├── tests/
│   ├── test_evm.py        # Kiểm thử EVM
│   ├── test_solana.py     # Kiểm thử Solana
│   └── test_wallets.py    # Kiểm thử ví
├── logs/
│   └── bot.log            # Nhật ký hoạt động
├── requirements.txt       # Danh sách thư viện
└── README.md              # Hướng dẫn

Reading

Json trong Python

1. Cơ bản về JSON và Python

JSON (JavaScript Object Notation) là một định dạng dữ liệu nhẹ, dễ đọc, được sử dụng để trao đổi dữ liệu giữa các hệ thống. Trong Python, JSON được xử lý thông qua module json.

Đặc điểm của JSON:

  • Dữ liệu JSON bao gồm các cặp key-value.
  • Các kiểu dữ liệu cơ bản:
    • Chuỗi (string): "name": "John"
    • Số (number): "age": 30
    • Boolean: "is_student": true
    • Mảng (array): "scores": [90, 85, 88]
    • Đối tượng (object): "address": {"street": "123 Main St", "city": "Hanoi"}
    • Null: "nickname": null

Module json trong Python

Module json cung cấp các phương thức để:

  • Chuyển đổi dữ liệu Python sang JSON (serialization).
  • Chuyển đổi JSON thành dữ liệu Python (deserialization).

Cách nhập module:

import json

2. Các thao tác cơ bản với JSON

2.1. Chuyển đổi từ Python sang JSON (Serialization)

Sử dụng json.dumps() để chuyển đổi một đối tượng Python (dict, list, str, int, float, bool, None) thành chuỗi JSON.

import json

data = {
    "name": "Nguyen Van A",
    "age": 25,
    "is_student": True,
    "scores": [90, 85, 88],
    "address": {"street": "123 Main St", "city": "Hanoi"}
}

json_string = json.dumps(data)
print(json_string)

# Định dạng đẹp (pretty print)
json_string_pretty = json.dumps(data, indent=4)
print(json_string_pretty)

2.2. Chuyển đổi từ JSON sang Python (Deserialization)

Sử dụng json.loads() để chuyển chuỗi JSON thành đối tượng Python.

import json

json_string = '{"name": "Nguyen Van A", "age": 25, "is_student": true}'

data = json.loads(json_string)
print(data)
print(data["name"])  # Output: Nguyen Van A

2.3. Làm việc với file JSON

Ghi JSON vào file (json.dump):

import json

data = {"name": "Nguyen Van A", "age": 25}
with open("data.json", "w") as file:
    json.dump(data, file, indent=4)

Đọc JSON từ file (json.load):

import json

with open("data.json", "r") as file:
    data = json.load(file)
print(data)

3. Các kỹ thuật nâng cao với JSON

3.1. Xử lý các kiểu dữ liệu phức tạp

Tùy chỉnh mã hóa JSON (default):

import json
from datetime import datetime

data = {"name": "Nguyen Van A", "created_at": datetime.now()}

def custom_serializer(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError("Type not serializable")

json_string = json.dumps(data, default=custom_serializer)
print(json_string)

Tùy chỉnh giải mã JSON (object_hook):

import json

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

def object_hook(dict_data):
    if "name" in dict_data and "age" in dict_data:
        return User(dict_data["name"], dict_data["age"])
    return dict_data

json_string = '{"name": "Nguyen Van A", "age": 25}'
user = json.loads(json_string, object_hook=object_hook)
print(user.name, user.age)

3.2. Xử lý JSON lớn với ijson

Cài đặt:

pip install ijson

Ví dụ:

import ijson

with open("large_data.json", "r") as file:
    parser = ijson.items(file, "item")
    for item in parser:
        print(item)

3.3. Xử lý lỗi JSON

import json

invalid_json = '{"name": "Nguyen Van A", "age": 25'  # Thiếu dấu }

try:
    data = json.loads(invalid_json)
except json.JSONDecodeError as e:
    print(f"Error decoding JSON: {e}")

3.4. Tối ưu hóa hiệu suất với orjson hoặc ujson

Cài đặt:

pip install orjson

Ví dụ với orjson:

import orjson

data = {"name": "Nguyen Van A", "age": 25}
json_bytes = orjson.dumps(data)
print(json_bytes.decode())

3.5. Làm việc với API và JSON

import requests

response = requests.get("https://api.github.com/users/octocat")
data = response.json()
print(data["login"])

4. Ứng dụng thực tế

4.1. Lưu trữ cấu hình

import json

config = {
    "database": {"host": "localhost", "port": 5432},
    "debug": True
}

with open("config.json", "w") as file:
    json.dump(config, file, indent=4)

with open("config.json", "r") as file:
    config = json.load(file)
print(config["database"]["host"])

4.2. Xây dựng API với Flask

from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/api/user")
def get_user():
    user = {"name": "Nguyen Van A", "age": 25}
    return jsonify(user)

if __name__ == "__main__":
    app.run()

4.3. Phân tích dữ liệu lớn (log, API, NoSQL)

import json

log_data = '''
[
    {"timestamp": "2025-04-21T10:00:00", "event": "login", "user": "Nguyen Van A"},
    {"timestamp": "2025-04-21T10:01:00", "event": "logout", "user": "Nguyen Van A"}
]
'''

logs = json.loads(log_data)
for log in logs:
    print(f"{log['timestamp']}: {log['event']} by {log['user']}")

5. Tài liệu và tài nguyên học thêm

Học qua dự án:

  • Xây dựng ứng dụng lưu trữ danh bạ sử dụng JSON.
  • Tạo một API đơn giản với Flask hoặc FastAPI.
  • Phân tích dữ liệu từ API công khai như GitHub hoặc OpenWeather.

Tóm tắt

  • Cơ bản: json.dumps, json.loads, json.dump, json.load.
  • Nâng cao: Tùy chỉnh mã hóa/giải mã, xử lý dữ liệu lớn (ijson), tối ưu (orjson), làm việc với API.
  • Thực hành: Lưu trữ cấu hình, xây dựng API, phân tích dữ liệu.

Cơ bản về nhật kí (log) trong Python

Ghi nhật ký cơ bản trong Python liên quan đến việc sử dụng mô-đun logging có sẵn để ghi lại các sự kiện, lỗi hoặc thông tin trong quá trình thực thi chương trình. Đây là một cách mạnh mẽ để theo dõi những gì đang xảy ra trong mã của bạn, gỡ lỗi và giám sát hành vi ứng dụng mà không làm lộn xộn đầu ra với các lệnh print. Dưới đây là giải thích về cách hoạt động và cách áp dụng hiệu quả trong một dự án.

Cơ bản về ghi nhật ký trong Python

Mô-đun logging cung cấp một khung linh hoạt với các thành phần chính sau:

  • Loggers: Các đối tượng bạn sử dụng để ghi lại thông điệp. Mỗi logger có một tên, thường phân cấp (ví dụ: myapp.module1).
  • Handlers: Quyết định nơi các thông điệp nhật ký được gửi đến (ví dụ: màn hình, tệp, mạng).
  • Formatters: Xác định cấu trúc của đầu ra nhật ký (ví dụ: dấu thời gian, mức độ nhật ký, thông điệp).

Mức độ nhật ký

Chỉ ra mức độ nghiêm trọng của thông điệp:

  • DEBUG (10): Thông tin chi tiết để gỡ lỗi.
  • INFO (20): Xác nhận rằng mọi thứ đang hoạt động.
  • WARNING (30): Điều gì đó bất ngờ nhưng không nghiêm trọng.
  • ERROR (40): Một vấn đề cần chú ý.
  • CRITICAL (50): Một thất bại nghiêm trọng.

Theo mặc định, ghi nhật ký của Python bắt đầu từ mức WARNING, vì vậy các thông điệp DEBUGINFO sẽ không hiển thị trừ khi bạn cấu hình khác đi.

Ví dụ đơn giản

import logging

# Cấu hình cơ bản
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("myapp")

# Ghi lại một số thông điệp
logger.debug("Điều này sẽ không hiển thị theo mặc định")
logger.info("Bắt đầu chương trình")
logger.warning("Có gì đó có thể không ổn")
logger.error("Đã xảy ra lỗi!")

Đầu ra (với level=logging.INFO):

INFO:myapp:Bắt đầu chương trình
WARNING:myapp:Có gì đó có thể không ổn
ERROR:myapp:Đã xảy ra lỗi!

Cách áp dụng ghi nhật ký hiệu quả trong một dự án

1. Thiết lập cấu hình phù hợp

Sử dụng basicConfig cho các tập lệnh nhỏ, nhưng đối với các dự án lớn hơn, hãy cấu hình ghi nhật ký theo chương trình hoặc qua tệp cấu hình.

Ví dụ với một handler tệp:

import logging

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)  # Thiết lập mức độ của logger

# Tạo một handler tệp
fh = logging.FileHandler("app.log")
fh.setLevel(logging.DEBUG)

# Tạo một handler màn hình
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)

# Xác định một formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# Thêm handler vào logger
logger.addHandler(fh)
logger.addHandler(ch)

logger.debug("Thông tin gỡ lỗi cho tệp nhật ký")
logger.info("Thông tin chung cho cả màn hình và tệp")
logger.error("Thông điệp lỗi ở mọi nơi")

Đầu ra trong app.log:

2025-04-01 12:00:00,123 - myapp - DEBUG - Thông tin gỡ lỗi cho tệp nhật ký
2025-04-01 12:00:00,124 - myapp - INFO - Thông tin chung cho cả màn hình và tệp
2025-04-01 12:00:00,125 - myapp - ERROR - Thông điệp lỗi ở mọi nơi

Đầu ra màn hình:

2025-04-01 12:00:00,124 - myapp - INFO - Thông tin chung cho cả màn hình và tệp
2025-04-01 12:00:00,125 - myapp - ERROR - Thông điệp lỗi ở mọi nơi

2. Sử dụng Logger có tên

Trong một dự án với nhiều mô-đun, sử dụng tên logger phân cấp để phân biệt giữa các thành phần. Ví dụ:

logger = logging.getLogger(__name__)

Điều này giúp truy vết nguồn gốc của thông điệp nhật ký.

3. Ghi lại thông tin có ý nghĩa

NÊN: Ghi lại các sự kiện quan trọng.

KHÔNG: Lạm dụng DEBUG hoặc ghi dữ liệu nhạy cảm.

Ví dụ:

try:
    result = 10 / 0
except ZeroDivisionError:
    logger.error("Phát hiện chia cho số không", exc_info=True)

4. Điều chỉnh mức độ nhật ký cho các môi trường

import os
env = os.getenv("ENV", "dev")
logger.setLevel(logging.DEBUG if env == "dev" else logging.INFO)

5. Xoay vòng nhật ký cho các dự án lớn

from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler("app.log", maxBytes=1024*1024, backupCount=5)
handler.setFormatter(formatter)
logger.addHandler(handler)

6. Tập trung hóa cấu hình ghi nhật ký

# logging_config.py
import logging

def setup_logging():
    logger = logging.getLogger("myapp")
    logger.setLevel(logging.DEBUG)
    handler = logging.FileHandler("app.log")
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    return logger

Sau đó nhập nó:

from logging_config import setup_logging
logger = setup_logging()
logger.info("Ứng dụng đã khởi động")

7. Kiểm tra ghi nhật ký của bạn

Đảm bảo nhật ký được ghi như kỳ vọng bằng cách sử dụng unit test.

Tại sao nó hiệu quả

  • Gỡ lỗi: Nhật ký chỉ ra chính xác lỗi.
  • Giám sát: Nhật ký INFO theo dõi hoạt động, ERROR đánh dấu vấn đề.
  • Khả năng mở rộng: Ghi nhật ký có cấu trúc thích nghi với sự phát triển của dự án.

Hãy bắt đầu với ghi nhật ký cơ bản trên màn hình, sau đó mở rộng với các handlerformatter khi dự án của bạn yêu cầu!

Bất đồng bộ (asynchronous) trong Python

Trong Python, "bất đồng bộ" là cách viết mã cho phép các tác vụ chạy độc lập mà không cần đợi nhau hoàn thành.
Thông thường, Python thực thi mã từng dòng (đồng bộ), nên nếu một tác vụ (như tải file) mất thời gian, chương trình sẽ đợi đến khi xong mới chuyển sang dòng tiếp theo.

Lập trình bất đồng bộ, sử dụng asyncawait, cho phép chương trình bắt đầu một tác vụ và tiếp tục làm việc khác trong khi tác vụ đó chạy nền.
Điều này rất hữu ích cho các tác vụ như lấy dữ liệu từ web, đọc file, hoặc xử lý nhiều yêu cầu, nơi việc đợi sẽ làm chậm chương trình.

Các khái niệm quan trọng

  • async: Đánh dấu một hàm là bất đồng bộ, nghĩa là nó có thể tạm dừng để mã khác chạy trong khi chờ đợi (như thao tác I/O).
  • await: Tạm dừng hàm bất đồng bộ tại điểm đó cho đến khi tác vụ được chờ (như gọi mạng) hoàn thành, mà không làm tắc nghẽn phần còn lại của chương trình.

Ví dụ minh họa:
Hãy nghĩ như nấu ăn:

  • Đồng bộ là bạn đợi nước sôi rồi mới thái rau.
  • Bất đồng bộ là bạn đun nước, thái rau trong lúc đợi, và quay lại khi nước sôi.

Ứng dụng thực tế

Một trường hợp phổ biến là gửi nhiều yêu cầu web.

  • Nếu không dùng async, bạn phải đợi từng yêu cầu hoàn thành trước khi bắt đầu cái tiếp theo.
  • Với asyncawait, bạn có thể gửi tất cả yêu cầu cùng lúc và xử lý phản hồi khi chúng đến, tiết kiệm thời gian.

Ví dụ đơn giản

Dưới đây là ví dụ sử dụng asyncio (thư viện bất đồng bộ của Python) để mô phỏng việc lấy dữ liệu từ hai "trang web" cùng lúc:

import asyncio

# Mô phỏng một tác vụ mất thời gian (như lấy dữ liệu từ web)
async def fetch_data(site):
    print(f"Bắt đầu lấy dữ liệu từ {site}...")
    await asyncio.sleep(2)  # Mô phỏng độ trễ 2 giây (ví dụ: yêu cầu mạng)
    print(f"Đã lấy xong dữ liệu từ {site}")
    return f"Dữ liệu từ {site}"

# Hàm bất đồng bộ chính để chạy nhiều tác vụ
async def main():
    # Bắt đầu cả hai tác vụ "cùng lúc"
    task1 = fetch_data("Site A")
    task2 = fetch_data("Site B")
    
    # Chờ cả hai hoàn thành và lấy kết quả
    results = await asyncio.gather(task1, task2)
    print("Hoàn thành tất cả! Kết quả:", results)

# Chạy chương trình bất đồng bộ
asyncio.run(main())

Kết quả:

Bắt đầu lấy dữ liệu từ Site A...
Bắt đầu lấy dữ liệu từ Site B...
Đã lấy xong dữ liệu từ Site A
Đã lấy xong dữ liệu từ Site B
Hoàn thành tất cả! Kết quả: ['Dữ liệu từ Site A', 'Dữ liệu từ Site B']

Chuyện gì đang xảy ra?

  1. fetch_data là hàm bất đồng bộ giả lập việc lấy dữ liệu với độ trễ 2 giây (await asyncio.sleep(2)).
  2. Trong main, chúng ta bắt đầu cả hai "lần lấy dữ liệu" mà không cần đợi cái này xong mới làm cái kia.
  3. asyncio.gather chạy chúng đồng thời và đợi tất cả hoàn thành.
  4. Tổng thời gian khoảng 2 giây (không phải 4), vì các tác vụ chồng lấp nhau.

Đây là ví dụ đơn giản, nhưng trong thực tế, bạn có thể thay asyncio.sleep bằng aiohttp.get để lấy dữ liệu web thật.
Async tỏa sáng khi xử lý nhiều tác vụ phụ thuộc I/O như vậy!


Lược đồ đơn giản: Đồng bộ vs Bất đồng bộ

1. Đồng bộ (Synchronous)

Thời gian: 0s   2s   4s   6s
Task 1:   [----]         (Chờ 2 giây)
Task 2:         [----]   (Chờ 2 giây)
Tổng: 4 giây
  • Task 1 bắt đầu, mất 2 giây, chương trình đợi.
  • Task 2 chỉ bắt đầu sau khi Task 1 xong, mất thêm 2 giây.
  • Tổng thời gian: 4 giây.

2. Bất đồng bộ (Asynchronous)

Thời gian: 0s   2s   4s
Task 1:   [----]      
Task 2:   [----]      
Tổng: 2 giây
  • Task 1 và Task 2 bắt đầu cùng lúc.
  • Cả hai chạy song song, mỗi cái mất 2 giây.
  • Tổng thời gian: chỉ 2 giây, vì chúng chồng lấp.

Giải thích hình dung

Hãy tưởng tượng bạn là một đầu bếp trong bếp:

Đồng bộ:

  1. Bạn đun nước (Task 1) và đứng đợi 2 phút cho nước sôi, không làm gì khác.
  2. Sau đó, bạn thái rau (Task 2) trong 2 phút.
  3. Tổng cộng: 4 phút.

Bất đồng bộ:

  1. Bạn bật bếp đun nước (Task 1), rồi ngay lập tức đi thái rau (Task 2).
  2. Trong khi nước sôi (2 phút), bạn đã thái xong rau (cũng 2 phút).
  3. Sau 2 phút, cả nước sôi lẫn rau thái đều xong.
  4. Tổng cộng: chỉ 2 phút.

asyncawait trong Python giống như bạn "giao việc" cho bếp đun nước và kiểm tra lại khi cần (await), thay vì đứng đợi.


Minh họa qua mã ví dụ trước

async def fetch_data(site):
    print(f"Bắt đầu lấy dữ liệu từ {site}...")
    await asyncio.sleep(2)  # Chờ 2 giây
    print(f"Đã lấy xong dữ liệu từ {site}")
    return f"Dữ liệu từ {site}"

async def main():
    results = await asyncio.gather(fetch_data("Site A"), fetch_data("Site B"))
    print("Kết quả:", results)

asyncio.run(main())

Lược đồ dòng thời gian:

Thời gian: 0s       2s
Site A:   [--------]  (Bắt đầu -> Xong)
Site B:   [--------]  (Bắt đầu -> Xong)
Tổng: 2 giây
  • 0s: Cả "Site A" và "Site B" bắt đầu lấy dữ liệu.
  • Trong 2 giây: Cả hai chạy song song.
  • 2s: Cả hai hoàn thành cùng lúc, tổng thời gian chỉ 2 giây.