Bài 12: Modules và Packages (Phần 1)
Mục Tiêu Bài Học
Sau khi hoàn thành bài này, bạn sẽ:
- ✅ Hiểu module là gì và tại sao cần dùng
- ✅ Import modules với các cách khác nhau
- ✅ Tạo modules riêng của bạn
- ✅ Sử dụng
__name__và__main__ - ✅ Tổ chức code thành modules
Module Là Gì?
Module là file Python (.py) chứa code có thể tái sử dụng (functions, classes, variables).
Tại sao dùng modules?
- ✅ Code Organization - Chia code thành các file logic
- ✅ Reusability - Dùng lại code ở nhiều projects
- ✅ Namespace - Tránh xung đột tên
- ✅ Maintainability - Dễ maintain và debug
- ✅ Collaboration - Team work hiệu quả hơn
# Không có modules - tất cả trong 1 file# main.py - 5000 lines of code! def calculate_tax(): pass def send_email(): pass def validate_user(): pass # ... 4950 more lines # Có modules - organized# tax.pydef calculate_tax(): pass # email.pydef send_email(): pass # auth.pydef validate_user(): pass # main.py - clean!import taximport emailimport auth
Import Modules
Import Cơ Bản
# 1. Import toàn bộ moduleimport math print(math.pi) # 3.141592653589793print(math.sqrt(16)) # 4.0print(math.pow(2, 3)) # 8.0 # 2. Import với aliasimport datetime as dt today = dt.date.today()print(today) # 2025-10-27 # 3. Import specific itemsfrom math import pi, sqrt print(pi) # 3.141592653589793print(sqrt(16)) # 4.0# print(math.pow(2, 3)) # NameError - chỉ import pi và sqrt # 4. Import tất cả với *from math import * print(pi) # Worksprint(sqrt(16)) # Worksprint(cos(0)) # Works# ⚠️ Không khuyến khích - pollutes namespace
Import Paths
Python tìm modules theo thứ tự:
- Current directory - thư mục hiện tại
- PYTHONPATH - environment variable
- Standard library - built-in modules
- Site-packages - installed packages
# Check search pathsimport sysfor path in sys.path: print(path) # Add custom pathsys.path.append('/path/to/custom/modules')
Common Standard Library Modules
# Math operationsimport mathprint(math.factorial(5)) # 120 # Random numbersimport randomprint(random.randint(1, 100))print(random.choice(['a', 'b', 'c'])) # Date and timeimport datetimenow = datetime.datetime.now()print(now.strftime("%Y-%m-%d %H:%M:%S")) # Operating systemimport osprint(os.getcwd()) # Current directoryprint(os.listdir('.')) # List files # JSONimport jsondata = {"name": "Alice", "age": 25}json_string = json.dumps(data)print(json_string) # Regular expressionsimport repattern = r'\d+'text = "I have 2 cats and 3 dogs"numbers = re.findall(pattern, text)print(numbers) # ['2', '3']
Tạo Module Riêng
Module Đơn Giản
# File: math_utils.py"""Math utility functions. This module provides basic math operations.""" def add(a, b): """Add two numbers.""" return a + b def subtract(a, b): """Subtract b from a.""" return a - b def multiply(a, b): """Multiply two numbers.""" return a * b def divide(a, b): """ Divide a by b. Returns None if b is zero. """ if b == 0: return None return a / b # Module-level variablePI = 3.14159 # File: main.pyimport math_utils result = math_utils.add(10, 5)print(result) # 15 result = math_utils.multiply(3, 4)print(result) # 12 print(math_utils.PI) # 3.14159
Module với Classes
# File: user.py"""User management module.""" class User: """User class.""" def __init__(self, name, email): self.name = name self.email = email def greet(self): return f"Hello, I'm {self.name}" def __str__(self): return f"User({self.name}, {self.email})" def create_user(name, email): """Factory function to create user.""" return User(name, email) def validate_email(email): """Validate email format.""" return "@" in email and "." in email.split("@")[1] # File: main.pyfrom user import User, create_user, validate_email # Use classuser1 = User("Alice", "[email protected]")print(user1.greet()) # Use functionuser2 = create_user("Bob", "[email protected]")print(user2) # Validateprint(validate_email("[email protected]")) # Trueprint(validate_email("invalid.email")) # False
Module Organization
# File: string_utils.py"""String utility functions.""" def reverse(text): """Reverse a string.""" return text[::-1] def capitalize_words(text): """Capitalize each word.""" return " ".join(word.capitalize() for word in text.split()) def remove_spaces(text): """Remove all spaces.""" return text.replace(" ", "") def count_words(text): """Count words in text.""" return len(text.split()) def is_palindrome(text): """Check if text is palindrome.""" text = text.lower().replace(" ", "") return text == text[::-1] # Private function (convention)def _helper_function(): """This is private - don't import.""" pass # File: main.pyimport string_utils text = "hello world"print(string_utils.reverse(text))print(string_utils.capitalize_words(text))print(string_utils.count_words(text))print(string_utils.is_palindrome("radar"))
__name__ và __main__
Understanding __name__
# File: example.pyprint(f"__name__ = {__name__}") # Run directly: python example.py# Output: __name__ = __main__ # Import from another fileimport example# Output: __name__ = example
if __name__ == "__main__":
Pattern quan trọng nhất trong Python modules!
# File: calculator.py"""Simple calculator module.""" def add(a, b): return a + b def subtract(a, b): return a - b def multiply(a, b): return a * b def divide(a, b): if b == 0: return None return a / b # Code chỉ chạy khi run directlyif __name__ == "__main__": # Test code print("Testing calculator...") print(f"10 + 5 = {add(10, 5)}") print(f"10 - 5 = {subtract(10, 5)}") print(f"10 * 5 = {multiply(10, 5)}") print(f"10 / 5 = {divide(10, 5)}") print("All tests passed!") # Run directly: python calculator.py# Output: Testing calculator... (shows test results) # Import in another file: import calculator# No output - test code không chạy!
Practical Use Cases
# File: data_processor.py"""Data processing utilities.""" def clean_data(data): """Remove None values.""" return [x for x in data if x is not None] def calculate_average(numbers): """Calculate average.""" if not numbers: return 0 return sum(numbers) / len(numbers) def find_outliers(numbers, threshold=2): """Find outliers using simple threshold.""" avg = calculate_average(numbers) return [x for x in numbers if abs(x - avg) > threshold * avg] # Demo và testingif __name__ == "__main__": # Demo code print("=== Data Processor Demo ===") # Test clean_data dirty_data = [1, 2, None, 3, None, 4] clean = clean_data(dirty_data) print(f"Clean data: {clean}") # Test average numbers = [10, 20, 30, 40, 50] avg = calculate_average(numbers) print(f"Average: {avg}") # Test outliers data_with_outliers = [10, 12, 11, 10, 100, 11, 12] outliers = find_outliers(data_with_outliers) print(f"Outliers: {outliers}")
Module Attributes
Module-Level Variables
# File: config.py"""Configuration module.""" # ConstantsAPP_NAME = "My Application"VERSION = "1.0.0"DEBUG = True # Configuration dictDATABASE = { "host": "localhost", "port": 5432, "name": "mydb"} # API settingsAPI_KEY = "secret_key_123"API_URL = "https://api.example.com" # File: main.pyimport config print(config.APP_NAME)print(config.VERSION)print(config.DATABASE["host"]) if config.DEBUG: print("Debug mode is ON")
Special Attributes
# File: my_module.py"""This is my module. It demonstrates special attributes.""" def my_function(): """A simple function.""" pass if __name__ == "__main__": # Module name print(f"Name: {__name__}") # Module docstring print(f"Doc: {__doc__}") # Module file path print(f"File: {__file__}") # Module directory import os print(f"Dir: {os.path.dirname(__file__)}")
Ví Dụ Thực Tế
1. Validator Module
# File: validators.py"""Input validation utilities.""" import re def is_valid_email(email): """ Validate email address. Args: email (str): Email to validate Returns: bool: True if valid """ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return bool(re.match(pattern, email)) def is_valid_phone(phone): """ Validate phone number (Vietnam format). Args: phone (str): Phone number Returns: bool: True if valid """ # Remove common separators phone = phone.replace("-", "").replace(" ", "").replace(".", "") # Check if starts with 0 and has 10 digits return phone.startswith("0") and len(phone) == 10 and phone.isdigit() def is_valid_password(password): """ Validate password strength. Requirements: - At least 8 characters - Contains uppercase - Contains lowercase - Contains digit - Contains special character Args: password (str): Password to validate Returns: tuple: (bool, str) - (is_valid, message) """ if len(password) < 8: return False, "Password must be at least 8 characters" if not any(c.isupper() for c in password): return False, "Password must contain uppercase letter" if not any(c.islower() for c in password): return False, "Password must contain lowercase letter" if not any(c.isdigit() for c in password): return False, "Password must contain digit" special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?" if not any(c in special_chars for c in password): return False, "Password must contain special character" return True, "Password is strong" def is_valid_url(url): """ Validate URL format. Args: url (str): URL to validate Returns: bool: True if valid """ pattern = r'^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/.*)?$' return bool(re.match(pattern, url)) # Testingif __name__ == "__main__": print("=== Validator Tests ===") # Email tests print(f"[email protected]: {is_valid_email('[email protected]')}") print(f"invalid.email: {is_valid_email('invalid.email')}") # Phone tests print(f"0123456789: {is_valid_phone('0123456789')}") print(f"012-345-6789: {is_valid_phone('012-345-6789')}") # Password tests valid, msg = is_valid_password("Weak") print(f"'Weak': {valid} - {msg}") valid, msg = is_valid_password("Strong@Pass123") print(f"'Strong@Pass123': {valid} - {msg}") # URL tests print(f"https://example.com: {is_valid_url('https://example.com')}") print(f"not a url: {is_valid_url('not a url')}")
2. File Utils Module
# File: file_utils.py"""File operation utilities.""" import osimport json def read_file(filepath): """ Read text file. Args: filepath (str): Path to file Returns: str: File contents or None if error """ try: with open(filepath, 'r', encoding='utf-8') as f: return f.read() except FileNotFoundError: print(f"File not found: {filepath}") return None except Exception as e: print(f"Error reading file: {e}") return None def write_file(filepath, content): """ Write text to file. Args: filepath (str): Path to file content (str): Content to write Returns: bool: True if success """ try: with open(filepath, 'w', encoding='utf-8') as f: f.write(content) return True except Exception as e: print(f"Error writing file: {e}") return False def read_json(filepath): """ Read JSON file. Args: filepath (str): Path to JSON file Returns: dict/list: Parsed JSON or None if error """ try: with open(filepath, 'r', encoding='utf-8') as f: return json.load(f) except FileNotFoundError: print(f"File not found: {filepath}") return None except json.JSONDecodeError: print(f"Invalid JSON in: {filepath}") return None def write_json(filepath, data, indent=2): """ Write data to JSON file. Args: filepath (str): Path to file data: Data to write (dict/list) indent (int): JSON indentation Returns: bool: True if success """ try: with open(filepath, 'w', encoding='utf-8') as f: json.dump(data, f, indent=indent, ensure_ascii=False) return True except Exception as e: print(f"Error writing JSON: {e}") return False def get_file_size(filepath): """ Get file size in bytes. Args: filepath (str): Path to file Returns: int: File size or None if error """ try: return os.path.getsize(filepath) except Exception as e: print(f"Error getting file size: {e}") return None def list_files(directory, extension=None): """ List files in directory. Args: directory (str): Directory path extension (str): Filter by extension (e.g., '.txt') Returns: list: List of filenames """ try: files = os.listdir(directory) if extension: files = [f for f in files if f.endswith(extension)] return files except Exception as e: print(f"Error listing files: {e}") return [] # Demoif __name__ == "__main__": print("=== File Utils Demo ===") # Write and read text file write_file("test.txt", "Hello, World!") content = read_file("test.txt") print(f"File content: {content}") # Write and read JSON data = {"name": "Alice", "age": 25, "city": "Hanoi"} write_json("test.json", data) loaded_data = read_json("test.json") print(f"JSON data: {loaded_data}") # Get file size size = get_file_size("test.txt") print(f"File size: {size} bytes") # List files files = list_files(".", extension=".py") print(f"Python files: {files}")
3. Math Utils Module
# File: math_utils.py"""Advanced math utilities.""" def is_prime(n): """ Check if number is prime. Args: n (int): Number to check Returns: bool: True if prime """ if n < 2: return False if n == 2: return True if n % 2 == 0: return False # Check odd divisors up to sqrt(n) i = 3 while i * i <= n: if n % i == 0: return False i += 2 return True def get_primes(start, end): """ Get all primes in range. Args: start (int): Start of range end (int): End of range Returns: list: List of prime numbers """ return [n for n in range(start, end + 1) if is_prime(n)] def factorial(n): """ Calculate factorial. Args: n (int): Non-negative integer Returns: int: n! (factorial) """ if n < 0: raise ValueError("Factorial not defined for negative numbers") if n <= 1: return 1 result = 1 for i in range(2, n + 1): result *= i return result def fibonacci(n): """ Get nth Fibonacci number (iterative). Args: n (int): Position (0-indexed) Returns: int: nth Fibonacci number """ if n <= 1: return n a, b = 0, 1 for _ in range(2, n + 1): a, b = b, a + b return b def gcd(a, b): """ Calculate Greatest Common Divisor. Args: a (int): First number b (int): Second number Returns: int: GCD of a and b """ while b: a, b = b, a % b return abs(a) def lcm(a, b): """ Calculate Least Common Multiple. Args: a (int): First number b (int): Second number Returns: int: LCM of a and b """ return abs(a * b) // gcd(a, b) # Testingif __name__ == "__main__": print("=== Math Utils Demo ===") # Prime tests print(f"Is 17 prime? {is_prime(17)}") print(f"Primes 1-20: {get_primes(1, 20)}") # Factorial print(f"5! = {factorial(5)}") # Fibonacci print(f"10th Fibonacci: {fibonacci(10)}") # GCD and LCM print(f"GCD(48, 18) = {gcd(48, 18)}") print(f"LCM(48, 18) = {lcm(48, 18)}")
Best Practices
# 1. Module docstring ở đầu file"""This is module docstring. Describe what the module does.""" # 2. Imports ở đầu, grouped# Standard libraryimport osimport sys # Third-partyimport requestsimport numpy # Local modulesimport my_module # 3. Constants UPPERCASEMAX_SIZE = 100DEFAULT_TIMEOUT = 30 # 4. Private functions với _def _internal_helper(): """Private function - not for public use.""" pass # 5. Public API rõ ràng__all__ = ['public_function1', 'public_function2'] # 6. Sử dụng if __name__ == "__main__"if __name__ == "__main__": # Test code here pass
Bài Tập Thực Hành
Bài 1: String Utils Module
Tạo string_utils.py:
reverse(text)- Reverse stringis_palindrome(text)- Check palindromecount_chars(text)- Count character frequency- Test trong
if __name__ == "__main__"
Bài 2: List Utils Module
Tạo list_utils.py:
flatten(nested)- Flatten nested listchunk(lst, size)- Split into chunksunique(lst)- Remove duplicates (preserve order)- Demo với test data
Bài 3: Date Utils Module
Tạo date_utils.py:
get_days_between(date1, date2)- Days between datesis_weekend(date)- Check if weekendformat_date(date, format)- Format date- Test với datetime module
Bài 4: Calculator Module
Tạo calculator.py:
- Basic operations (+, -, *, /)
- Advanced (power, sqrt, factorial)
- History tracking
- Command-line interface trong
__main__
Bài 5: Config Module
Tạo config.py:
- Load config from JSON file
- Get/set config values
- Save config
- Default values handling
Tóm Tắt
✅ Module: File Python (.py) chứa code tái sử dụng
✅ import module: Import toàn bộ module
✅ from module import item: Import specific items
✅ import module as alias: Import với alias
✅ __name__ == "__main__": Code chỉ chạy khi run directly
✅ Module docstring: Document module purpose
✅ __all__: Define public API
Bài Tiếp Theo
Remember:
- Một module = một file
.py - Module name = filename (không có
.py) - Dùng
if __name__ == "__main__"cho test code - Module docstring ở đầu file
- Organize imports: standard → third-party → local!