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^expcount_down(n)- Print countdown from n to 1sum_digits(n)- Sum digits of number
Bài 4: Closure
Viết closure:
make_counter(start)- Counter starting from startmake_greeter(greeting)- Greeter with custom greetingmake_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!