Bài 12: Modules và Packages (Phần 2)
Mục Tiêu Bài Học
Sau khi hoàn thành bài này, bạn sẽ:
- ✅ Hiểu package là gì và cách tổ chức
- ✅ Tạo packages với
__init__.py - ✅ Sử dụng pip để cài đặt packages
- ✅ Tạo và quản lý virtual environments
- ✅ Làm việc với
requirements.txt
Package Là Gì?
Package là thư mục chứa nhiều modules và file __init__.py.
my_package/ __init__.py # Makes it a package module1.py # Module 1 module2.py # Module 2 subpackage/ __init__.py # Subpackage module3.py # Module in subpackage
Package vs Module:
- Module: File Python đơn lẻ (
.py) - Package: Thư mục chứa modules +
__init__.py
Tại Sao Dùng Packages?
# Không có package - flat structureproject/ utils.py validators.py formatters.py parsers.py converters.py # ... 50 more files # Có packages - organizedproject/ utils/ __init__.py string_utils.py file_utils.py date_utils.py validators/ __init__.py email.py phone.py password.py formatters/ __init__.py json_formatter.py xml_formatter.py
Tạo Package
Basic Package Structure
# Project structuremyapp/ mypackage/ __init__.py module1.py module2.py main.py # File: mypackage/module1.pydef hello(): return "Hello from module1" def add(a, b): return a + b # File: mypackage/module2.pydef goodbye(): return "Goodbye from module2" def multiply(a, b): return a * b # File: mypackage/__init__.py# Empty file (makes it a package) # File: main.pyfrom mypackage import module1, module2 print(module1.hello()) # Hello from module1print(module2.goodbye()) # Goodbye from module2print(module1.add(5, 3)) # 8print(module2.multiply(4, 2)) # 8
__init__.py - Package Initialization
__init__.py chạy khi package được import.
# File: mypackage/__init__.py"""My Package - A collection of utilities. This package provides various utility functions.""" print("Initializing mypackage...") # Package-level variableVERSION = "1.0.0" # Import commonly used itemsfrom .module1 import hello, addfrom .module2 import goodbye, multiply # Define public API__all__ = ['hello', 'goodbye', 'add', 'multiply', 'VERSION'] # File: main.pyimport mypackage# Output: Initializing mypackage... # Direct accessprint(mypackage.hello()) # Hello from module1print(mypackage.VERSION) # 1.0.0 # Hoặcfrom mypackage import hello, addprint(hello()) # Hello from module1print(add(5, 3)) # 8
Relative Imports
Relative imports dùng trong packages.
# Project structuremyapp/ mypackage/ __init__.py core.py utils.py helpers/ __init__.py formatter.py # File: mypackage/core.pyfrom .utils import process_data # Same levelfrom .helpers import formatter # Subpackage def main(): data = process_data([1, 2, 3]) formatted = formatter.format_data(data) return formatted # File: mypackage/utils.pydef process_data(data): return [x * 2 for x in data] # File: mypackage/helpers/formatter.pyfrom ..utils import process_data # Parent package def format_data(data): return f"Data: {data}" # Relative import syntax:# . = current package# .. = parent package# ... = parent's parent package
Subpackages
# Project structuremyproject/ utils/ __init__.py string/ __init__.py operations.py validators.py math/ __init__.py basic.py advanced.py main.py # File: utils/string/operations.pydef reverse(text): return text[::-1] def capitalize_words(text): return text.title() # File: utils/string/validators.pydef is_palindrome(text): text = text.lower().replace(" ", "") return text == text[::-1] # File: utils/string/__init__.pyfrom .operations import reverse, capitalize_wordsfrom .validators import is_palindrome __all__ = ['reverse', 'capitalize_words', 'is_palindrome'] # File: utils/__init__.pyfrom . import stringfrom . import math # File: main.pyfrom utils.string import reverse, is_palindrome print(reverse("hello")) # ollehprint(is_palindrome("radar")) # True # Orimport utilsprint(utils.string.reverse("world")) # dlrow
pip - Python Package Manager
pip là tool để install, uninstall, và manage Python packages.
Basic pip Commands
# Check pip versionpip --version# pip 24.0 from /usr/local/lib/python3.11/site-packages/pip # Install packagepip install requests # Install specific versionpip install requests==2.31.0 # Install minimum versionpip install requests>=2.30.0 # Upgrade packagepip install --upgrade requests # Uninstall packagepip uninstall requests # Show package infopip show requests # List installed packagespip list # Search for packages (deprecated, use pypi.org)# pip search package_name
Popular Python Packages
# Web frameworkspip install djangopip install flaskpip install fastapi # Data sciencepip install numpypip install pandaspip install matplotlib # Testingpip install pytestpip install unittest2 # HTTP requestspip install requestspip install httpx # Databasepip install psycopg2-binary # PostgreSQLpip install pymongo # MongoDB # Utilitiespip install python-dotenv # Environment variablespip install pillow # Image processingpip install beautifulsoup4 # Web scraping
Using Installed Packages
# After: pip install requestsimport requests # Make HTTP requestresponse = requests.get('https://api.github.com')print(response.status_code) # 200print(response.json()) # JSON data # After: pip install python-dotenvfrom dotenv import load_dotenvimport os load_dotenv() # Load .env fileapi_key = os.getenv('API_KEY') # After: pip install pillowfrom PIL import Image img = Image.open('photo.jpg')img.thumbnail((200, 200))img.save('thumbnail.jpg')
Virtual Environments
Virtual Environment là isolated Python environment cho mỗi project.
Tại Sao Cần Virtual Environment?
# ❌ Không có venv - conflicts# Project A needs Django 3.2# Project B needs Django 4.2# Cannot install both globally! # ✅ Có venv - isolatedproject_a/ venv/ # Django 3.2 app.py project_b/ venv/ # Django 4.2 app.py
Tạo Virtual Environment
# Using venv (built-in, Python 3.3+)python -m venv myenv # Or with specific namepython -m venv venvpython -m venv .venvpython -m venv env # Directory structure createdmyenv/ bin/ # Unix/Mac Scripts/ # Windows lib/ include/ pyvenv.cfg
Activate Virtual Environment
# macOS/Linuxsource myenv/bin/activate # Windows (CMD)myenv\Scripts\activate.bat # Windows (PowerShell)myenv\Scripts\Activate.ps1 # After activation(myenv) $ python --version(myenv) $ which python# /path/to/myenv/bin/python
Deactivate Virtual Environment
# Any OSdeactivate # Returns to global Python$ python --version$ which python# /usr/bin/python
Virtual Environment Workflow
# 1. Create projectmkdir myprojectcd myproject # 2. Create virtual environmentpython -m venv venv # 3. Activatesource venv/bin/activate # macOS/Linux # 4. Install packages(venv) $ pip install django(venv) $ pip install requests(venv) $ pip install pytest # 5. Check installed packages(venv) $ pip list # 6. Work on project(venv) $ python app.py # 7. Deactivate when done(venv) $ deactivate
requirements.txt
requirements.txt lists all dependencies.
Creating requirements.txt
# Generate from current environmentpip freeze > requirements.txt # View contentscat requirements.txt# Django==4.2.7# requests==2.31.0# pytest==7.4.3
Format của requirements.txt
# Basic formatDjango==4.2.7requests==2.31.0 # With comments# Web frameworkDjango==4.2.7 # HTTP libraryrequests==2.31.0 # Version specifiersDjango>=4.2.0,<5.0.0 # Rangerequests>=2.30 # Minimumpytest==7.4.* # Any patch version # From git repositorygit+https://github.com/user/repo.git@branch # From local directory./local-package # With extrasrequests[security]django[argon2]
Using requirements.txt
# Install all dependenciespip install -r requirements.txt # Upgrade allpip install --upgrade -r requirements.txt # Install in editable mode (development)pip install -e .
Different Requirements Files
# Project structuremyproject/ requirements/ base.txt # Base dependencies dev.txt # Development only prod.txt # Production only requirements.txt # All dependencies # File: requirements/base.txtDjango==4.2.7requests==2.31.0python-dotenv==1.0.0 # File: requirements/dev.txt-r base.txt # Include basepytest==7.4.3black==23.11.0flake8==6.1.0 # File: requirements/prod.txt-r base.txt # Include basegunicorn==21.2.0psycopg2-binary==2.9.9 # Installpip install -r requirements/dev.txt # Developmentpip install -r requirements/prod.txt # Production
Ví Dụ Thực Tế
1. Complete Package Example
# Project structuremylib/ setup.py README.md mylib/ __init__.py core.py utils/ __init__.py string_utils.py math_utils.py tests/ __init__.py test_core.py requirements.txt # File: mylib/__init__.py"""MyLib - A utility library. Version: 1.0.0""" __version__ = "1.0.0"__author__ = "Your Name" from .core import process, analyzefrom .utils import string_utils, math_utils __all__ = ['process', 'analyze', 'string_utils', 'math_utils'] # File: mylib/core.pyfrom .utils.string_utils import clean_textfrom .utils.math_utils import calculate_stats def process(data): """Process data.""" cleaned = [clean_text(item) for item in data] return cleaned def analyze(numbers): """Analyze numbers.""" return calculate_stats(numbers) # File: mylib/utils/string_utils.pydef clean_text(text): """Clean and normalize text.""" return text.strip().lower() def split_words(text): """Split text into words.""" return text.split() # File: mylib/utils/math_utils.pydef calculate_stats(numbers): """Calculate basic statistics.""" return { 'count': len(numbers), 'sum': sum(numbers), 'avg': sum(numbers) / len(numbers) if numbers else 0, 'min': min(numbers) if numbers else None, 'max': max(numbers) if numbers else None } # File: mylib/utils/__init__.pyfrom . import string_utilsfrom . import math_utils # Usagefrom mylib import process, analyze data = [" Hello ", " World "]print(process(data)) # ['hello', 'world'] numbers = [1, 2, 3, 4, 5]print(analyze(numbers))# {'count': 5, 'sum': 15, 'avg': 3.0, 'min': 1, 'max': 5}
2. Django-like Project Structure
# Project structuremyproject/ venv/ myproject/ __init__.py settings.py urls.py wsgi.py apps/ users/ __init__.py models.py views.py urls.py posts/ __init__.py models.py views.py urls.py utils/ __init__.py validators.py helpers.py manage.py requirements.txt .env # File: myproject/settings.py"""Project settings.""" import osfrom pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key') DEBUG = os.getenv('DEBUG', 'True') == 'True' INSTALLED_APPS = [ 'apps.users', 'apps.posts',] # File: utils/validators.py"""Validation utilities.""" import re def validate_email(email): pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return bool(re.match(pattern, email)) # File: apps/users/models.py"""User models.""" from utils.validators import validate_email class User: def __init__(self, email, name): if not validate_email(email): raise ValueError("Invalid email") self.email = email self.name = name # File: requirements.txtDjango==4.2.7python-dotenv==1.0.0psycopg2-binary==2.9.9 # File: .envSECRET_KEY=your-secret-key-hereDEBUG=TrueDATABASE_URL=postgresql://user:pass@localhost/dbname
3. Data Processing Package
# Project structuredataprocessor/ venv/ dataprocessor/ __init__.py loader.py cleaner.py analyzer.py exporter.py examples/ example.py data.csv requirements.txt # File: dataprocessor/__init__.py"""Data Processor Package. A package for loading, cleaning, and analyzing data.""" __version__ = "1.0.0" from .loader import load_csv, load_jsonfrom .cleaner import clean_data, remove_nullsfrom .analyzer import calculate_stats, find_outliersfrom .exporter import export_csv, export_json __all__ = [ 'load_csv', 'load_json', 'clean_data', 'remove_nulls', 'calculate_stats', 'find_outliers', 'export_csv', 'export_json'] # File: dataprocessor/loader.py"""Data loading utilities.""" import jsonimport csv def load_csv(filepath): """Load data from CSV file.""" with open(filepath, 'r') as f: reader = csv.DictReader(f) return list(reader) def load_json(filepath): """Load data from JSON file.""" with open(filepath, 'r') as f: return json.load(f) # File: dataprocessor/cleaner.py"""Data cleaning utilities.""" def clean_data(data): """Clean data by removing empty strings.""" return [{k: v for k, v in item.items() if v} for item in data] def remove_nulls(data): """Remove null values from data.""" return [item for item in data if all(item.values())] # File: dataprocessor/analyzer.py"""Data analysis utilities.""" def calculate_stats(numbers): """Calculate basic statistics.""" return { 'count': len(numbers), 'sum': sum(numbers), 'avg': sum(numbers) / len(numbers), 'min': min(numbers), 'max': max(numbers) } def find_outliers(numbers, threshold=2): """Find outliers in data.""" avg = sum(numbers) / len(numbers) return [x for x in numbers if abs(x - avg) > threshold * avg] # File: examples/example.py"""Example usage.""" from dataprocessor import load_csv, clean_data, calculate_stats # Load datadata = load_csv('data.csv') # Clean dataclean = clean_data(data) # Analyzeages = [int(row['age']) for row in clean if 'age' in row]stats = calculate_stats(ages) print(f"Statistics: {stats}") # File: requirements.txt# No external dependencies needed for this example
4. API Client Package
# Project structureapiclient/ venv/ apiclient/ __init__.py client.py auth.py exceptions.py models/ __init__.py user.py post.py tests/ test_client.py requirements.txt # File: apiclient/__init__.py"""API Client Package. A simple API client for interacting with REST APIs.""" __version__ = "1.0.0" from .client import APIClientfrom .auth import BasicAuth, TokenAuthfrom .exceptions import APIError, AuthenticationError __all__ = ['APIClient', 'BasicAuth', 'TokenAuth', 'APIError'] # File: apiclient/client.py"""API client implementation.""" import requestsfrom .exceptions import APIError class APIClient: """Simple API client.""" def __init__(self, base_url, auth=None): self.base_url = base_url self.auth = auth self.session = requests.Session() def get(self, endpoint): """Make GET request.""" url = f"{self.base_url}{endpoint}" response = self.session.get(url, auth=self.auth) if response.status_code != 200: raise APIError(f"Request failed: {response.status_code}") return response.json() def post(self, endpoint, data): """Make POST request.""" url = f"{self.base_url}{endpoint}" response = self.session.post(url, json=data, auth=self.auth) if response.status_code not in [200, 201]: raise APIError(f"Request failed: {response.status_code}") return response.json() # File: apiclient/auth.py"""Authentication methods.""" from requests.auth import HTTPBasicAuth class BasicAuth(HTTPBasicAuth): """Basic authentication.""" pass class TokenAuth: """Token-based authentication.""" def __init__(self, token): self.token = token def __call__(self, request): request.headers['Authorization'] = f'Bearer {self.token}' return request # File: apiclient/exceptions.py"""Custom exceptions.""" class APIError(Exception): """Base API error.""" pass class AuthenticationError(APIError): """Authentication failed.""" pass # File: requirements.txtrequests==2.31.0 # Usage examplefrom apiclient import APIClient, TokenAuth client = APIClient( base_url="https://api.example.com", auth=TokenAuth("your-token-here")) users = client.get("/users")print(users)
Best Practices
# 1. Package structuremypackage/ __init__.py # Always include core.py # Main functionality utils.py # Utilities exceptions.py # Custom exceptions constants.py # Constants # 2. Clear __init__.py# File: __init__.py"""Package description.""" __version__ = "1.0.0"__author__ = "Your Name" from .core import main_functionfrom .utils import helper_function __all__ = ['main_function', 'helper_function'] # 3. Use relative imports trong packagefrom .utils import helper # Goodfrom mypackage.utils import helper # Avoid # 4. Separate requirementsrequirements/ base.txt dev.txt prod.txt # 5. Always use virtual environmentpython -m venv venvsource venv/bin/activatepip install -r requirements.txt # 6. Document dependenciespip freeze > requirements.txt # 7. .gitignore for venv# File: .gitignorevenv/__pycache__/*.pyc.env
Bài Tập Thực Hành
Bài 1: Create Package
Tạo package mathtools:
basic.py- Basic operationsadvanced.py- Advanced operations__init__.py- Package initialization- Test trong separate file
Bài 2: Virtual Environment Setup
- Tạo project mới
- Setup virtual environment
- Install: requests, pytest
- Create requirements.txt
- Deactivate và recreate từ requirements.txt
Bài 3: Subpackage Structure
Tạo package với subpackages:
myapp/ utils/ string/ math/ date/
Bài 4: Package với CLI
Tạo package:
- Core functionality
- Command-line interface
if __name__ == "__main__"in__main__.py
Bài 5: Full Project
Tạo complete project:
- Virtual environment
- Multiple packages
- requirements.txt (base, dev, prod)
- .env for config
- README.md
- Tests
Tóm Tắt
✅ Package: Thư mục + __init__.py
✅ __init__.py: Chạy khi package import
✅ Relative imports: .module (same level), ..module (parent)
✅ pip: pip install package_name
✅ Virtual env: python -m venv venv
✅ Activate: source venv/bin/activate (macOS/Linux)
✅ requirements.txt: pip freeze > requirements.txt
✅ Install deps: pip install -r requirements.txt
Bài Tiếp Theo
Bài 13: File I/O - Read/write files, working với text, CSV, JSON, và binary files.
Remember:
- Always use virtual environments!
- One venv per project
- Track dependencies in requirements.txt
- Never commit venv/ to git
__init__.pymakes directory a package- Use relative imports in packages!