Module và package trong Python: import, pip, venv và thư viện nói đơn giản
Module trong Python là một tệp .py, package là thư mục chứa các module, còn thư viện là bộ package phục vụ một mục đích; các thứ này được nạp bằng lệnh import. Thư viện bên thứ ba được cài bằng pip (hoặc uv) vào môi trường ảo venv — thư mục riêng chứa trình thông dịch và package của một dự án, để các phiên bản của các dự án khác nhau không xung đột.
Module trong Python là một tệp chứa code, còn import là cách lấy các hàm và class từ tệp đó về dùng. Từ các module người ta ghép thành package, từ package thành thư viện; để thư viện bên thứ ba của các dự án khác nhau không xung đột, chúng được cài vào môi trường ảo venv. Dưới đây là toàn bộ chuỗi: chuyện gì xảy ra khi import, package được tổ chức thế nào, pip và venv hoạt động ra sao, và lazy import mới trong Python 3.15 là gì.
Module, package, thư viện: khác nhau ở đâu
| Khái niệm | Đó là gì | Ví dụ |
|---|---|---|
| Module | Một tệp .py; tên module là tên tệp không có đuôi | danhmuc.py → import danhmuc |
| Package | Thư mục chứa các module và tệp __init__.py | chitieu/bao_cao.py → from chitieu.bao_cao import tao |
| Thư viện | Bộ module và package có sẵn cho một nhiệm vụ nào đó | requests, pandas, pathlib trong thư viện chuẩn |
| Distribution (gói trên PyPI) | Tệp nén mà pip cài đặt | pip install requests |
Trong giao tiếp hằng ngày, các từ «thư viện», «package» và «module» thường bị dùng lẫn lộn — điều đó là bình thường. Điều quan trọng là: về mặt kỹ thuật, import luôn nạp một module hoặc package, còn pip cài một distribution từ PyPI, bên trong có các package.
Import trong Python hoạt động thế nào
Có nhiều dạng import, và tất cả đều xuất hiện trong code thực tế:
import math # toàn bộ module, gọi math.sqrt
from math import sqrt, pi # từng tên riêng
import datetime as dt # bí danh
from collections import Counter as C
print(math.sqrt(16), sqrt(9), pi)
print(dt.date.today())
Bí danh được dùng rộng rãi với các thư viện lớn: import numpy as np, import pandas as pd. Không nên dùng from module import *: nó làm rối không gian tên và che mất nguồn gốc của các tên.
Thực tế import làm gì
- Tìm module trong bộ nhớ đệm
sys.modules. Nếu đã nạp rồi thì trả về ngay. - Nếu chưa, tìm tệp trong các thư mục của
sys.path: thư mục của script đang chạy, thư viện chuẩn, site-packages của môi trường. - Thực thi toàn bộ tệp đó từ trên xuống dưới.
- Đưa đối tượng module vào bộ nhớ đệm và gắn nó với tên.
Từ đây có hai kết luận thực tế. Lần import thứ hai của cùng một module gần như không tốn gì — code không chạy lại. Và toàn bộ code ở cấp cao nhất của module, kể cả print và các phép tính nặng, sẽ chạy ngay khi import.
import sys
for p in sys.path:
print(p)
print("math" in sys.modules)
if __name__ == "__main__"
Vì import thực thi tệp, cần một cách phân biệt «tôi được chạy như script» với «tôi được import». Đó là vai trò của biến __name__: khi chạy trực tiếp tệp, nó bằng "__main__"; khi được import, nó bằng tên module.
# danhmuc.py
def doan(title: str) -> str:
return "ăn uống" if "cà phê" in title.lower() else "khác"
if __name__ == "__main__":
print(doan("Quán cà phê gần nhà")) # chỉ chạy khi gọi python danhmuc.py
Với package có một lựa chọn khác: đặt tệp __main__.py bên trong và chạy bằng python -m chitieu.
Module chuẩn của Python
Python đi kèm «sẵn pin»: hàng trăm module tích hợp dùng được ngay mà không cần cài đặt. Những module thông dụng nhất:
| Module | Dùng để |
|---|---|
os, sys, pathlib, shutil | Hệ thống, đường dẫn, tệp |
json, csv, pickle | Định dạng dữ liệu |
datetime, time, calendar | Ngày và giờ |
math, random, statistics, fractions | Số học |
re, string | Xử lý văn bản |
collections, itertools, functools | Cấu trúc dữ liệu và iterator |
logging, argparse, subprocess | Log, tham số dòng lệnh, chạy chương trình khác |
asyncio, threading | Bất đồng bộ và luồng |
Danh sách đầy đủ các module của thư viện chuẩn nằm trong sys.stdlib_module_names (Python 3.10 trở lên). Xem một module có gì bên trong bằng dir(math) và help(math).
Package riêng: cấu trúc dự án
Khi số module tăng lên, chúng được gom vào một package. Cách bố trí phổ biến là src-layout:
chi-tieu-project/
├── pyproject.toml
└── src/
└── chitieu/
├── __init__.py
├── cli.py
├── phan_tich.py
├── danhmuc.py
└── bao_cao.py
Bên trong package, các module import nhau bằng tên package (from chitieu.danhmuc import doan) hoặc tương đối (from .danhmuc import doan). Import tương đối chỉ hoạt động bên trong package, không hoạt động trong tệp được chạy trực tiếp như một script.
pip trong Python: cài thư viện
Thư viện bên thứ ba nằm trên PyPI, và pip — công cụ đi kèm Python — dùng để cài chúng. Cách chắc ăn nhất là gọi pip thông qua chính interpreter, như vậy thư viện chắc chắn được cài vào đúng Python mà bạn dùng để chạy code.
python -m pip install requests # cài đặt
python -m pip install "pandas>=2.3" # kèm ràng buộc phiên bản
python -m pip install --upgrade requests
python -m pip uninstall requests # gỡ bỏ
python -m pip list # danh sách package đã cài
python -m pip show requests # phiên bản và phụ thuộc
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt
Trên Windows, người ta hay dùng trình khởi chạy py thay cho python. Nếu không có pip, module tích hợp sẽ cài nó: python -m ensurepip --upgrade.
Các thư viện Python phổ biến
Trên PyPI có hàng trăm nghìn package, nhưng đa số dự án chỉ dùng một danh sách ngắn gọn. Nên bắt đầu từ đó, thay vì tìm những thứ lạ.
| Nhiệm vụ | Thư viện |
|---|---|
| Gọi HTTP và API | requests, httpx |
| Phân tích dữ liệu và bảng | pandas, numpy |
| Biểu đồ | matplotlib |
| Dịch vụ web | fastapi, django, flask |
| Kiểm thử | pytest |
| Ảnh | pillow (import dưới tên PIL) |
Lưu ý: tên khi cài và tên khi import không phải lúc nào cũng giống nhau. Cài bằng pip install pillow nhưng viết from PIL import Image; cài opencv-python nhưng import cv2. Nếu import lỗi ngay sau khi cài, hãy kiểm tra trong tài liệu của package xem nó được import dưới tên nào.
Cài thư viện trên Linux
Trên Debian và Ubuntu đời mới, lệnh pip install vào Python hệ thống sẽ báo lỗi externally-managed-environment (PEP 668). Đây là cơ chế bảo vệ: Python hệ thống thuộc về trình quản lý gói của hệ điều hành, pip không nên làm hỏng nó. Cách đúng không phải là --break-system-packages, mà là dùng môi trường ảo.
venv trong Python: môi trường ảo
Môi trường ảo là một thư mục riêng, có bản sao (liên kết) của trình thông dịch và thư mục site-packages riêng. Các thư viện cài trong đó chỉ được dự án này nhìn thấy. Nhờ vậy, một dự án có thể dùng Django 4, còn dự án bên cạnh dùng Django 5, mà không xung đột.
Module venv được tích hợp sẵn trong Python, không cần cài thêm (trên Debian/Ubuntu đôi khi cần gói hệ thống python3-venv).
# tạo môi trường trong thư mục .venv
python -m venv .venv
# kích hoạt: Linux và macOS
source .venv/bin/activate
# kích hoạt: Windows PowerShell
.venv\Scripts\Activate.ps1
# từ giờ pip cài package vào trong .venv
python -m pip install requests
python -c "import sys; print(sys.prefix)"
# thoát khỏi môi trường
deactivate
Sau khi kích hoạt, đầu dòng lệnh xuất hiện (.venv). Thư mục môi trường không đưa lên git (thêm .venv/ vào .gitignore) và không chuyển sang máy khác — thay vào đó, người ta lưu danh sách phụ thuộc và tạo lại môi trường. Nếu PowerShell chặn script kích hoạt, cho phép cho người dùng hiện tại: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.
uv và pyproject.toml
Các dự án hiện đại khai báo phụ thuộc trong pyproject.toml. Trình quản lý uv được dùng trong khóa học gộp việc tạo môi trường và cài đặt lại với nhau: uv add pandas tải package, ghi nó vào pyproject.toml và cố định phiên bản chính xác trong uv.lock. Đồng nghiệp clone dự án, chạy uv sync và có ngay môi trường giống hệt.
[project]
name = "chitieu"
version = "0.1.0"
requires-python = ">=3.15"
dependencies = [
"pandas>=2.3",
]
[project.scripts]
chitieu = "chitieu.cli:main"
Lazy import trong Python 3.15 (PEP 810)
Điểm mới lớn nhất của 3.15 trong chủ đề này là từ khóa lazy. Các thư viện nặng nạp rất chậm: trong phép đo của khóa học trên image python:3.15-slim, một import pandas thông thường mất khoảng 415 ms, còn lazy import pandas chỉ khoảng 13 ms. Với một tiện ích dòng lệnh chỉ cần chạy để xem --help, chênh lệch này rất lớn.
lazy import pandas as pd
lazy from chitieu.bao_cao import tao_bao_cao
def main() -> None:
...
df = pd.read_csv(path) # việc nạp pandas thực sự diễn ra ở đây
Thay vì nạp module ngay, tên được gắn với một đối tượng proxy (types.LazyImportType), và import thực sự chỉ diễn ra khi tên được dùng lần đầu. Nếu không bao giờ dùng đến, thời gian đó được tiết kiệm hoàn toàn.
Giới hạn của lazy import
- Chỉ dùng được ở cấp cao nhất của module: bên trong hàm, class hoặc
try/exceptsẽ gâySyntaxError. lazy from x import *bị cấm.ModuleNotFoundErrorkhông xuất hiện khi khởi động mà xuất hiện ở lần sử dụng đầu tiên.- Các tác dụng phụ của module (đăng ký plugin và tương tự) bị dịch chuyển theo thời gian.
Có thể làm mọi import thành lazy bằng cờ -X lazy_imports=all hoặc biến môi trường PYTHON_LAZY_IMPORTS=all, còn ngoại lệ được chỉ định qua sys.set_lazy_imports_filter(). Với code cần chạy cả trên phiên bản cũ, có thuộc tính module __lazy_modules__ = ["pandas"]: 3.15 sẽ làm các import được liệt kê thành lazy, còn phiên bản cũ chỉ thực hiện import thông thường. Muốn biết thời gian khởi động đi đâu, dùng python -X importtime -c "import pandas". Lợi ích rõ nhất là với các chương trình khởi động nhiều lần ngắn; một server chạy lâu hầu như không được hưởng lợi.
Lỗi thường gặp
ModuleNotFoundError: No module named 'requests'— thư viện được cài vào Python khác, hoặc môi trường chưa được kích hoạt. Kiểm tra bằngpython -m pip show requestsvới đúng interpreter đó.- Tệp của bạn có tên
json.py,random.pyhoặccsv.py— thư mục của script đứng đầu trongsys.path, nên nó che module chuẩn. Python 3.13 trở lên gợi ý «consider renaming» trong thông báo lỗi. Hãy đổi tên tệp và xóa__pycache__. - Import vòng tròn:
a.pyimportb.py, vàb.pylại importa.py. Hãy chuyển phần chung sang module thứ ba, hoặc import bên trong hàm. - Chạy
pipbên trong interpreter Python (>>> pip install) — sẽ gặpSyntaxError. Các lệnh pip phải chạy trong terminal.
Tổng kết
Module là một tệp, package là thư mục chứa module, thư viện là bộ sưu tập đã sẵn sàng; import tìm module trong bộ nhớ đệm và trong sys.path rồi thực thi nó một lần. Thư viện bên thứ ba chỉ nên cài bằng pip hoặc uv vào môi trường ảo venv — như vậy các dự án không xung đột và môi trường dễ tái tạo. Chủ đề này được thực hành trong khóa học Python: trình theo dõi chi tiêu trở thành một package có phụ thuộc, và khởi động nhanh nhờ lazy import.
Câu hỏi thường gặp
Có cần tạo venv cho mỗi dự án không?
Nên làm vậy: mỗi dự án có phiên bản thư viện riêng, và môi trường riêng giúp tránh xung đột cũng như không làm bẩn Python hệ thống.
pip khác uv ở điểm nào?
pip chỉ cài package, còn uv còn tạo môi trường, ghi phụ thuộc vào pyproject.toml và cố định phiên bản trong file lock; đồng thời nó nhanh hơn đáng kể.
Làm sao xem danh sách thư viện đã cài trong Python?
Chạy python -m pip list trong đúng môi trường; pip freeze cho cùng danh sách ở định dạng requirements.txt.
Các module đã cài nằm ở đâu?
Trong thư mục site-packages của môi trường hiện tại; đường dẫn tới một module cụ thể có trong thuộc tính __file__, ví dụ import requests; print(requests.__file__).
Có thể chuyển thư mục venv sang máy khác không?
Không, môi trường gắn với đường dẫn và interpreter của máy đó; người ta chỉ chuyển danh sách phụ thuộc và tạo lại môi trường.
Chủ đề này được giảng chi tiết kèm thực hành trong khóa học “Python 3.15: от новичка до профи”.
Đến khóa học