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____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ự:

  1. Current directory - thư mục hiện tại
  2. PYTHONPATH - environment variable
  3. Standard library - built-in modules
  4. 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____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 string
  • is_palindrome(text) - Check palindrome
  • count_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 list
  • chunk(lst, size) - Split into chunks
  • unique(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 dates
  • is_weekend(date) - Check if weekend
  • format_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

Bài 12.2: Packages và Virtual Environments - Package structure, __init__.py, pip, và virtual environments.


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!