Bỏ qua để đến nội dung

Hàm, chuỗi, đọc/ghi file

Sau bài này, bạn sẽ:

  • Định nghĩa và tổ chức hàm (functions) rõ ràng với tham số vị trí (positional), tham số từ khóa (keyword), giá trị mặc định và chú thích kiểu (type hints).
  • Nắm vững quy tắc phạm vi biến LEGB (Local, Enclosing, Global, Built-in) và cơ chế truyền tham số đối tượng (pass-by-assignment).
  • Hiểu tính bất biến (immutability) của chuỗi văn bản (str) và làm chủ các phương thức xử lý chuỗi phục vụ chuẩn hóa dữ liệu NLP.
  • Đọc và ghi tệp tin an toàn với trình quản lý ngữ cảnh (context manager with open(...)), xử lý chuẩn định dạng JSON và mã hóa ký tự UTF-8.

Khi một dự án AI lớn dần lên, bạn không thể viết hàng nghìn dòng lệnh trong một tệp duy nhất. Bạn cần:

  • Gom các đoạn code lặp đi lặp lại thành các hàm có thể tái sử dụng nhiều lần: hàm nạp dữ liệu, hàm tính sai số, hàm chuẩn hóa văn bản.
  • Đảm bảo rằng việc chỉnh sửa dữ liệu bên trong hàm không vô tình làm biến đổi dữ liệu ở ngoài phạm vi mong muốn.
  • Đọc các tệp dữ liệu huấn luyện từ đĩa cứng và lưu trữ trọng số mô hình hoặc nhật ký kết quả huấn luyện sau mỗi chu kỳ (epoch).

Mọi ứng dụng AI trong thực tế — từ pipeline tiền xử lý dữ liệu đến việc tải mô hình (checkpoint loader) — đều xoay quanh ba trụ cột: hàm để xử lý, chuỗi để biểu diễn văn bản, và hệ thống tệp để lưu trữ bền vững.

Một hàm chuẩn trong Python bắt đầu bằng từ khóa def, tên hàm, danh sách tham số và giá trị trả về return:

def compute_loss(y_true: float, y_pred: float) -> float:
"""Tính sai số bình phương giữa giá trị thực và giá trị dự đoán."""
return (y_true - y_pred) ** 2
  • Type hints: Giúp người đọc và các công cụ kiểm tra tĩnh (như MyPy) phát hiện sớm các lỗi sai kiểu dữ liệu mà không làm giảm tốc độ chạy của Python.
  • Tham số vị trí và tham số từ khóa: Bạn có thể gọi compute_loss(1.0, 0.8) hoặc gọi tường minh compute_loss(y_pred=0.8, y_true=1.0).
  • Tham số linh hoạt *args và **kwargs:
    • *args: Thu nhận các tham số vị trí phụ thành một tuple.
    • **kwargs: Thu nhận các tham số từ khóa phụ thành một dict.

Khi bạn tham chiếu một tên biến trong hàm, Python tìm kiếm theo quy tắc LEGB:

  1. L (Local): Các biến được định nghĩa ngay bên trong hàm hiện tại.
  2. E (Enclosing): Các biến thuộc hàm cha bao bọc (nếu hàm nằm lồng trong một hàm khác).
  3. G (Global): Các biến ở cấp độ tệp module.
  4. B (Built-in): Các từ khóa và hàm mặc định của Python (print, len, range).

Muốn sửa đổi một biến toàn cục từ bên trong thân hàm, bạn phải dùng từ khóa global. Tuy nhiên, trong lập trình AI chuyên nghiệp, việc lạm dụng biến toàn cục là điều tối kỵ vì nó khiến mã nguồn khó theo dõi và dễ gây ra hiệu ứng phụ ngoài ý muốn (side effects).

Chuỗi ký tự (str) trong Python là bất biến (immutable). Một khi chuỗi đã được tạo trong bộ nhớ, bạn không thể gán lại từng ký tự như danh sách (s[0] = 'H' sẽ gây lỗi TypeError).

Các phương thức chuẩn hóa chuỗi quan trọng cho NLP:

  • .strip(): Cắt bỏ khoảng trắng dư thừa ở đầu và cuối chuỗi.
  • .lower(): Chuyển toàn bộ chuỗi về chữ thường.
  • .replace(old, new): Thay thế chuỗi con.
  • .split(sep): Tách chuỗi thành danh sách các từ.
  • sep.join(list_of_strings): Nối danh sách các chuỗi thành một chuỗi duy nhất với ký tự ngăn cách.

Đọc và Ghi File An Toàn với Context Manager

Phần tiêu đề “Đọc và Ghi File An Toàn với Context Manager”

Khi mở một tệp trên hệ điều hành, chương trình nhận về một bộ định danh tệp (file descriptor). Nếu mở quá nhiều tệp mà không đóng lại, hệ điều hành sẽ cạn kiệt tài nguyên.

Cú pháp chuẩn mực để thao tác với tệp là sử dụng từ khóa with:

with open("data.txt", mode="r", encoding="utf-8") as f:
content = f.read()
# Ra khỏi khối with, tệp sẽ được tự động đóng ngay lập tức!

Các chế độ mở tệp phổ biến (mode):

  • "r": Đọc tệp văn bản (ném lỗi nếu tệp không tồn tại).
  • "w": Ghi tệp văn bản (xóa sạch nội dung cũ nếu tệp đã tồn tại, hoặc tạo mới nếu chưa có).
  • "a": Ghi nối tiếp vào cuối tệp (append).

Lưu trữ định dạng có cấu trúc với JSON: Thư viện chuẩn json cho phép bạn chuyển đổi qua lại giữa cấu trúc dữ liệu Python (dict, list) và chuỗi văn bản JSON thông qua hai hàm chính:

  • json.dump(obj, file): Ghi trực tiếp đối tượng ra tệp.
  • json.load(file): Đọc và giải mã dữ liệu từ tệp vào đối tượng Python.

Quy tắc tìm kiếm biến LEGB trong Python:

[ B - Built-in Scope ] (Hàm dựng sẵn: print, len, sum...)
▲
[ G - Global Scope ] (Biến ở cấp tệp ngoài cùng)
▲
[ E - Enclosing Scope ] (Hàm cha bên ngoài)
▲
[ L - Local Scope ] (Bên trong hàm đang thực thi)

Quy trình quản lý tệp an toàn với Context Manager with:

[ open("file.json", "w") ] ──► Gọi __enter__(): Mở tệp, cấp phát File Descriptor
│
▼
[ Khối lệnh thực thi ] ──► Ghi dữ liệu vào đĩa, xử lý logic...
│
├─► (Thành công bình thường) ┐
│ ├──► Gọi __exit__(): ĐÓNG TỆP TỰ ĐỘNG!
└─► (Phát sinh ngoại lệ Exception) ┘

Các ô dưới đây chạy độc lập. Bạn có thể nhấn nút Chạy để xem ngay kết quả.

Ô thứ nhất xây dựng một hàm tiền xử lý văn bản NLP hoàn chỉnh: sử dụng type hints, giá trị mặc định, phương thức chuỗi (strip, lower, replace, split) và gom nối chuỗi bằng join.

Hàm tiền xử lý và chuẩn hóa văn bản NLP

from typing import List
def clean_text(
raw_text: str,
lowercase: bool = True,
remove_punct: bool = True,
) -> List[str]:
# Chuẩn hóa khoảng trắng đầu cuối
text = raw_text.strip()
if lowercase:
text = text.lower()
# Loại bỏ các dấu câu phổ biến
if remove_punct:
for char in [".", ",", "!", "?", ";"]:
text = text.replace(char, "")
# Tách chuỗi thành danh sách các token
tokens = text.split()
return tokens
# Thử nghiệm hàm tiền xử lý với dữ liệu văn bản thô
sample = " AI and Deep Learning: Transforming the Future! "
cleaned = clean_text(sample)
print(f"Raw string length: {len(sample)}")
print(f"Cleaned tokens: {cleaned}")
print(f"Token count: {len(cleaned)}")
print(f"Joined text: {' '.join(cleaned)}")

Lần chạy đầu cần tải Python, khoảng 10 MB.

Đoạn mã nhận một chuỗi văn bản lộn xộn chứa khoảng trắng và dấu câu, tự động làm sạch và chuyển đổi thành danh sách các token chuẩn hóa. Đây là bước đầu tiên không thể thiếu trong mọi bài toán phân tích cảm xúc hay dịch máy.

Ô thứ hai mô phỏng việc lưu trữ và nạp lại nhật ký huấn luyện (training history) của mô hình học máy bằng định dạng JSON với câu lệnh ngữ cảnh with open(...).

Ghi và đọc nhật ký huấn luyện mô hình bằng JSON

import json
import os
import tempfile
# Dữ liệu nhật ký huấn luyện mô hình qua các epoch
history = {
"model": "SimpleLinear",
"epochs": 3,
"metrics": [
{"epoch": 1, "loss": 0.452, "val_loss": 0.490},
{"epoch": 2, "loss": 0.285, "val_loss": 0.310},
{"epoch": 3, "loss": 0.150, "val_loss": 0.182},
],
}
# Ghi nhật ký huấn luyện ra tệp JSON an toàn với context manager
filepath = os.path.join(tempfile.gettempdir(), "training_log.json")
with open(filepath, "w", encoding="utf-8") as f:
json.dump(history, f, indent=2)
# Đọc lại và kiểm tra số liệu đã lưu
with open(filepath, "r", encoding="utf-8") as f:
loaded_data = json.load(f)
last_epoch = loaded_data["metrics"][-1]
print(f"Model saved: {loaded_data['model']}")
print(f"Total epochs logged: {len(loaded_data['metrics'])}")
print(f"Final loss: {last_epoch['loss']:.4f}")
print(f"Final val_loss: {last_epoch['val_loss']:.4f}")
# Dọn dẹp tệp nhật ký tạm
os.remove(filepath)

Lần chạy đầu cần tải Python, khoảng 10 MB.

Dữ liệu lịch sử đo lường được chuyển đổi thành JSON, ghi xuống tệp tin tạm trên hệ thống rồi đọc ngược lại bộ nhớ một cách an toàn. Bạn có thể trích xuất chính xác sai số loss và val_loss của epoch cuối cùng để theo dõi tiến trình huấn luyện.

Câu 1/4 Cho hàm sau: def append_token(tok, tokens=[]): tokens.append(tok); return tokens. Kết quả của hai lần gọi append_token("a") rồi append_token("b") lần lượt là gì?
Câu 2/4 Quy tắc tìm kiếm phạm vi biến (scope) trong Python tuân theo thứ tự ưu tiên nào (từ gần nhất đến xa nhất)?
Câu 3/4 Khi thực hiện phép nối chuỗi trong một vòng lặp lớn gồm 100 000 lần bằng toán tử + (s = s + token), vì sao hiệu năng của chương trình bị giảm sút nghiêm trọng?
Câu 4/4 Lợi ích cốt lõi nhất của việc dùng cú pháp ngữ cảnh with open("data.csv", "r", encoding="utf-8") as f: khi làm việc với tệp là gì?

Ý chính:

  • Hàm đóng gói logic, sử dụng type hints để làm rõ ràng kiểu dữ liệu và ngăn chặn lỗi sớm.
  • Tránh xa bẫy đối số mặc định khả biến: luôn dùng None làm giá trị mặc định cho list hay dict.
  • Quy tắc phạm vi LEGB xác định quyền truy xuất biến từ cục bộ đến toàn cục.
  • Chuỗi văn bản là bất biến: thao tác biến đổi chuỗi luôn tạo ra đối tượng mới; dùng join() khi gom nhiều chuỗi.
  • Sử dụng with open(...) cùng encoding="utf-8" để đảm bảo an toàn tài nguyên tệp và tránh lỗi mã hóa ký tự.

Đọc thêm:

  • Python Software Foundation (2024). The Python Tutorial: Defining Functions. Tài liệu hướng dẫn định nghĩa hàm, đối số mặc định và tham số từ khóa.
  • Python Software Foundation (2024). PEP 484 – Type Hints. Đề xuất tiêu chuẩn chính thức của Python về hệ thống chú thích kiểu dữ liệu.
  • Python Software Foundation (2024). Reading and Writing Files. Hướng dẫn chính thức về thao tác với tệp, context manager và thư viện json.

Nội dung theo giấy phép CC BY-NC-SA 4.0. Mã nguồn theo giấy phép MIT.Giới thiệu