Bài 11: Functions - Hàm (Phần 2)

Mục Tiêu Bài Học

Sau khi hoàn thành bài này, bạn sẽ:

  • ✅ Sử dụng *args và **kwargs
  • ✅ Viết lambda functions
  • ✅ Hiểu variable scope (LEGB rule)
  • ✅ Áp dụng recursion
  • ✅ Làm việc với nested functions và closures

*args - Variable Positional Arguments

*args cho phép function nhận số lượng positional arguments không xác định.

Cú Pháp *args

# Basic *argsdef sum_all(*numbers):    """Sum all numbers."""    total = 0    for num in numbers:        total += num    return total print(sum_all(1, 2, 3))           # 6print(sum_all(1, 2, 3, 4, 5))     # 15print(sum_all(10))                # 10print(sum_all())                  # 0 # *args là tupledef print_args(*args):    print(f"Type: {type(args)}")    print(f"Args: {args}") print_args(1, 2, 3)# Type: <class 'tuple'># Args: (1, 2, 3)

Mix Parameters với *args

# Regular parameter trước *argsdef greet(greeting, *names):    for name in names:        print(f"{greeting}, {name}!") greet("Hello", "Alice", "Bob", "Charlie")# Hello, Alice!# Hello, Bob!# Hello, Charlie! # ❌ *args phải sau regular parameters# def invalid(*args, greeting):  # SyntaxError#     pass

Unpacking với *

# Unpack list/tuple thành argumentsdef add(a, b, c):    return a + b + c numbers = [1, 2, 3]result = add(*numbers)  # Unpack listprint(result)  # 6 # Combine values và unpackingvalues = [2, 3]result = add(1, *values)print(result)  # 6

**kwargs - Variable Keyword Arguments

**kwargs cho phép function nhận số lượng keyword arguments không xác định.

Cú Pháp **kwargs

# Basic **kwargsdef print_info(**info):    """Print all key-value pairs."""    for key, value in info.items():        print(f"{key}: {value}") print_info(name="Alice", age=25, city="Hanoi")# name: Alice# age: 25# city: Hanoi # **kwargs là dictdef show_kwargs(**kwargs):    print(f"Type: {type(kwargs)}")    print(f"Kwargs: {kwargs}") show_kwargs(a=1, b=2, c=3)# Type: <class 'dict'># Kwargs: {'a': 1, 'b': 2, 'c': 3}

Mix Parameters với **kwargs

# Combine all typesdef create_profile(name, age, *hobbies, **extra):    """    Create user profile.        Args:        name: Required name        age: Required age        *hobbies: Variable hobbies        **extra: Extra info    """    profile = {        "name": name,        "age": age,        "hobbies": list(hobbies),        "extra": extra    }    return profile profile = create_profile(    "Alice", 25,    "reading", "coding", "music",    city="Hanoi",    occupation="Developer")print(profile)# {#     'name': 'Alice',#     'age': 25,#     'hobbies': ['reading', 'coding', 'music'],#     'extra': {'city': 'Hanoi', 'occupation': 'Developer'}# }

Unpacking Dictionary với **

# Unpack dict thành keyword argumentsdef create_user(name, email, role):    return {        "name": name,        "email": email,        "role": role    } user_data = {    "name": "Alice",    "email": "[email protected]",    "role": "admin"} user = create_user(**user_data)  # Unpack dictprint(user) # Combine và overridedefaults = {"role": "user", "active": True}user = create_user(name="Bob", email="[email protected]", **defaults)print(user)

Lambda Functions

Lambda là anonymous function (function không tên), dùng cho operations đơn giản.

Cú Pháp Lambda

# Syntax: lambda parameters: expression # Regular functiondef square(x):    return x ** 2 # Lambda equivalentsquare_lambda = lambda x: x ** 2 print(square(5))         # 25print(square_lambda(5))  # 25 # Lambda với nhiều parametersadd = lambda a, b: a + bprint(add(3, 5))  # 8 # Lambda với default argumentgreet = lambda name, greeting="Hello": f"{greeting}, {name}!"print(greet("Alice"))          # Hello, Alice!print(greet("Bob", "Hi"))      # Hi, Bob!

Lambda với Built-in Functions

# sorted() với keystudents = [    {"name": "Alice", "grade": 85},    {"name": "Bob", "grade": 92},    {"name": "Charlie", "grade": 78}] # Sort by gradesorted_students = sorted(students, key=lambda s: s["grade"])print([s["name"] for s in sorted_students])# ['Charlie', 'Alice', 'Bob'] # Sort by namesorted_students = sorted(students, key=lambda s: s["name"])print([s["name"] for s in sorted_students])# ['Alice', 'Bob', 'Charlie'] # map() với lambdanumbers = [1, 2, 3, 4, 5]squared = list(map(lambda x: x ** 2, numbers))print(squared)  # [1, 4, 9, 16, 25] # filter() với lambdaevens = list(filter(lambda x: x % 2 == 0, numbers))print(evens)  # [2, 4] # reduce() với lambda (from functools)from functools import reducetotal = reduce(lambda acc, x: acc + x, numbers)print(total)  # 15

Lambda Limitations

# ❌ Lambda chỉ có 1 expression# lambda x: print(x); return x + 1  # SyntaxError # ❌ Không có statements# lambda x: if x > 0: return x  # SyntaxError # ✅ Dùng regular function cho logic phức tạpdef process(x):    if x > 0:        return x * 2    else:        return x / 2 # ✅ Lambda cho simple operationsdouble = lambda x: x * 2

Variable Scope - LEGB Rule

Scope xác định nơi variable có thể truy cập được.

LEGB Rule

Local → Enclosing → Global → Built-in

# Built-in scope (len, print, etc.)print(len("hello"))  # Built-in function # Global scopeglobal_var = "I'm global" def outer():    # Enclosing scope    enclosing_var = "I'm enclosing"        def inner():        # Local scope        local_var = "I'm local"                print(local_var)       # Local        print(enclosing_var)   # Enclosing        print(global_var)      # Global        print(len([1, 2, 3]))  # Built-in        inner() outer()

Local Scope

def my_function():    x = 10  # Local variable    print(x) my_function()  # 10# print(x)  # NameError: name 'x' is not defined # Local variables của mỗi call độc lậpdef counter():    count = 0    count += 1    print(count) counter()  # 1counter()  # 1 (không phải 2!)

Global Keyword

# Read global variablecount = 0 def show_count():    print(count)  # OK - read global show_count()  # 0 # ❌ Modify global - creates localdef increment():    count = count + 1  # UnboundLocalError # ✅ Use global keyworddef increment():    global count    count += 1 increment()print(count)  # 1 increment()print(count)  # 2

Nonlocal Keyword

# Modify enclosing scope variabledef outer():    count = 0        def increment():        nonlocal count  # Access enclosing scope        count += 1        print(f"Inner count: {count}")        increment()  # Inner count: 1    increment()  # Inner count: 2    print(f"Outer count: {count}")  # Outer count: 2 outer() # ❌ Không dùng nonlocal - tạo local variabledef outer():    count = 0        def increment():        count = count + 1  # UnboundLocalError        increment()

Recursion - Đệ Quy

Recursion là function gọi chính nó.

Cơ Bản Về Recursion

# Classic example: factorial# 5! = 5 * 4 * 3 * 2 * 1 = 120 def factorial(n):    # Base case - điều kiện dừng    if n == 0 or n == 1:        return 1        # Recursive case - gọi lại chính nó    return n * factorial(n - 1) print(factorial(5))  # 120# factorial(5) = 5 * factorial(4)#              = 5 * 4 * factorial(3)#              = 5 * 4 * 3 * factorial(2)#              = 5 * 4 * 3 * 2 * factorial(1)#              = 5 * 4 * 3 * 2 * 1#              = 120

Fibonacci Sequence

# Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21...# F(n) = F(n-1) + F(n-2) def fibonacci(n):    """Get nth Fibonacci number."""    # Base cases    if n == 0:        return 0    if n == 1:        return 1        # Recursive case    return fibonacci(n - 1) + fibonacci(n - 2) # Print first 10 Fibonacci numbersfor i in range(10):    print(fibonacci(i), end=" ")# 0 1 1 2 3 5 8 13 21 34

Recursion với Lists

# Sum list recursivelydef sum_list(numbers):    """Sum all numbers in list."""    # Base case: empty list    if not numbers:        return 0        # Recursive case: first + sum of rest    return numbers[0] + sum_list(numbers[1:]) print(sum_list([1, 2, 3, 4, 5]))  # 15 # Flatten nested listdef flatten(nested):    """Flatten nested list."""    result = []    for item in nested:        if isinstance(item, list):            result.extend(flatten(item))  # Recursive        else:            result.append(item)    return result nested_list = [1, [2, 3], [4, [5, 6]], 7]print(flatten(nested_list))  # [1, 2, 3, 4, 5, 6, 7]

Recursion vs Iteration

# Recursive approachdef factorial_recursive(n):    if n <= 1:        return 1    return n * factorial_recursive(n - 1) # Iterative approachdef factorial_iterative(n):    result = 1    for i in range(2, n + 1):        result *= i    return result # Both produce same resultprint(factorial_recursive(5))  # 120print(factorial_iterative(5))  # 120 # ⚠️ Recursion có recursion limitimport sysprint(sys.getrecursionlimit())  # 1000 (default) # Có thể thay đổi (cẩn thận!)# sys.setrecursionlimit(2000)

Nested Functions và Closures

Nested Functions

# Function inside functiondef outer(x):    def inner(y):        return x + y        return inner(10) print(outer(5))  # 15 # Multiple nested levelsdef level1():    def level2():        def level3():            return "Deep nesting!"        return level3()    return level2() print(level1())  # Deep nesting!

Closures

Closure là inner function ghi nhớ variables từ enclosing scope.

# Basic closuredef outer(x):    def inner(y):        return x + y  # 'x' from outer scope        return inner  # Return function, not call it add_5 = outer(5)add_10 = outer(10) print(add_5(3))   # 8  (5 + 3)print(add_10(3))  # 13 (10 + 3) # Closure retains statedef counter():    count = 0        def increment():        nonlocal count        count += 1        return count        return increment # Each counter is independentcounter1 = counter()counter2 = counter() print(counter1())  # 1print(counter1())  # 2print(counter2())  # 1 (independent!)print(counter1())  # 3

Practical Closure Examples

# 1. Function factorydef power(exponent):    """Create power function."""    def calculate(base):        return base ** exponent    return calculate square = power(2)cube = power(3) print(square(5))  # 25print(cube(5))    # 125 # 2. Multiplier factorydef multiplier(factor):    """Create multiplier function."""    return lambda x: x * factor double = multiplier(2)triple = multiplier(3) print(double(10))  # 20print(triple(10))  # 30 # 3. Validator factorydef make_validator(min_val, max_val):    """Create range validator."""    def validate(value):        return min_val <= value <= max_val    return validate age_validator = make_validator(0, 120)percent_validator = make_validator(0, 100) print(age_validator(25))    # Trueprint(age_validator(150))   # Falseprint(percent_validator(85)) # True

Ví Dụ Thực Tế

1. Flexible Calculator

def calculate(*numbers, operation="+"):    """    Calculate with variable numbers.        Args:        *numbers: Numbers to calculate        operation: '+', '-', '*', '/' (default: '+')        Returns:        Result of calculation    """    if not numbers:        return 0        result = numbers[0]        for num in numbers[1:]:        if operation == "+":            result += num        elif operation == "-":            result -= num        elif operation == "*":            result *= num        elif operation == "/":            if num != 0:                result /= num        return result print(calculate(1, 2, 3, 4, 5))              # 15print(calculate(10, 2, 3, operation="-"))     # 5print(calculate(2, 3, 4, operation="*"))      # 24print(calculate(100, 2, 5, operation="/"))    # 10.0

2. Dynamic Query Builder

def build_query(table, **conditions):    """    Build SQL-like query string.        Args:        table: Table name        **conditions: Column-value pairs        Returns:        Query string    """    query = f"SELECT * FROM {table}"        if conditions:        where_clauses = []        for column, value in conditions.items():            if isinstance(value, str):                where_clauses.append(f"{column}='{value}'")            else:                where_clauses.append(f"{column}={value}")                query += " WHERE " + " AND ".join(where_clauses)        return query # Usageprint(build_query("users"))# SELECT * FROM users print(build_query("users", age=25, city="Hanoi"))# SELECT * FROM users WHERE age=25 AND city='Hanoi' print(build_query("products", price=100, category="Electronics"))# SELECT * FROM products WHERE price=100 AND category='Electronics'

3. Memoization Decorator

def memoize(func):    """    Cache function results.        Args:        func: Function to memoize        Returns:        Memoized function    """    cache = {}        def wrapper(*args):        if args in cache:            print(f"Cache hit for {args}")            return cache[args]                print(f"Computing for {args}")        result = func(*args)        cache[args] = result        return result        return wrapper @memoizedef fibonacci(n):    if n <= 1:        return n    return fibonacci(n - 1) + fibonacci(n - 2) # First call - computesprint(fibonacci(5))# Computing for (5,)# Computing for (4,)# ...# 5 # Second call - uses cacheprint(fibonacci(5))# Cache hit for (5,)# 5

4. Retry Mechanism

def retry(max_attempts=3):    """    Retry decorator.        Args:        max_attempts: Maximum retry attempts        Returns:        Decorated function    """    def decorator(func):        def wrapper(*args, **kwargs):            attempts = 0            while attempts < max_attempts:                try:                    return func(*args, **kwargs)                except Exception as e:                    attempts += 1                    if attempts == max_attempts:                        print(f"Failed after {max_attempts} attempts")                        raise                    print(f"Attempt {attempts} failed: {e}. Retrying...")                return wrapper    return decorator @retry(max_attempts=3)def unreliable_function(value):    import random    if random.random() < 0.7:  # 70% chance of failure        raise ValueError("Random error")    return f"Success with {value}" # Trytry:    result = unreliable_function("test")    print(result)except ValueError:    print("Function failed completely")

5. Binary Search (Recursive)

def binary_search(arr, target, left=0, right=None):    """    Binary search using recursion.        Args:        arr: Sorted array        target: Value to find        left: Left index        right: Right index        Returns:        Index of target or -1 if not found    """    if right is None:        right = len(arr) - 1        # Base case: not found    if left > right:        return -1        # Find middle    mid = (left + right) // 2        # Found target    if arr[mid] == target:        return mid        # Search left half    if arr[mid] > target:        return binary_search(arr, target, left, mid - 1)        # Search right half    return binary_search(arr, target, mid + 1, right) # Testnumbers = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] print(binary_search(numbers, 7))   # 3print(binary_search(numbers, 15))  # 7print(binary_search(numbers, 20))  # -1 (not found)

Best Practices

# 1. *args/**kwargs naming convention# ✅ Use 'args' and 'kwargs'def function(*args, **kwargs):    pass # ❌ Confusing namesdef function(*items, **options):  # Works but not idiomatic    pass # 2. Lambda - keep it simple# ✅ Simple lambdasorted_list = sorted(data, key=lambda x: x[0]) # ❌ Complex lambda - use def insteadresult = list(map(lambda x: x * 2 if x > 0 else x / 2, data)) # 3. Recursion - always have base case# ✅ Has base casedef countdown(n):    if n <= 0:  # Base case        return    print(n)    countdown(n - 1) # ❌ No base case - infinite recursion!def bad_recursion(n):    print(n)    bad_recursion(n - 1)  # Never stops! # 4. Prefer iteration for simple loops# ❌ Unnecessary recursiondef sum_recursive(n):    if n == 0:        return 0    return n + sum_recursive(n - 1) # ✅ Simple iterationdef sum_iterative(n):    return sum(range(n + 1))

Bài Tập Thực Hành

Bài 1: *args và **kwargs

Viết function create_person(*names, **attributes):

  • names: Họ và tên (variable)
  • attributes: Các thuộc tính khác (age, city, etc.)
  • Return: Dictionary với full_name và attributes

Bài 2: Lambda Functions

Sử dụng lambda:

  • Sort list of dicts by multiple keys
  • Filter even numbers > 10
  • Map strings to uppercase

Bài 3: Recursion

Viết recursive functions:

  • power(base, exp) - Calculate base^exp
  • count_down(n) - Print countdown from n to 1
  • sum_digits(n) - Sum digits of number

Bài 4: Closure

Viết closure:

  • make_counter(start) - Counter starting from start
  • make_greeter(greeting) - Greeter with custom greeting
  • make_accumulator() - Accumulate values

Bài 5: Advanced Recursion

Viết function generate_permutations(items):

  • Input: List of items
  • Output: List of all permutations
  • Example: [1, 2, 3][[1,2,3], [1,3,2], [2,1,3], ...]

Tóm Tắt

*args: Variable positional arguments (tuple)
**kwargs: Variable keyword arguments (dict)
✅ Lambda: lambda params: expression - anonymous functions
✅ Scope: LEGB rule (Local → Enclosing → Global → Built-in)
global: Modify global variable
nonlocal: Modify enclosing variable
✅ Recursion: Function calls itself, needs base case
✅ Closure: Inner function remembers enclosing scope

Bài Tiếp Theo

Bài 12: Modules và Packages - Import, create modules, package structure, và virtual environments.


Remember:

  • *args for variable positional arguments
  • **kwargs for variable keyword arguments
  • Lambda for simple one-line functions
  • Recursion needs base case!
  • Closures remember enclosing scope
  • Prefer iteration over recursion when possible!