Bài 11: Functions - Hàm (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 function là gì và tại sao cần dùng
- ✅ Định nghĩa functions với def
- ✅ Làm việc với parameters và arguments
- ✅ Sử dụng return values
- ✅ Áp dụng default và keyword arguments
Function Là Gì?
Function là khối code có thể tái sử dụng, thực hiện một nhiệm vụ cụ thể.
Tại sao dùng functions?
- ✅ Reusability - Viết một lần, dùng nhiều lần
- ✅ Organization - Chia code thành các phần nhỏ
- ✅ Maintainability - Dễ sửa và update
- ✅ Testing - Dễ test từng phần
- ✅ Readability - Code dễ đọc hơn
# Không dùng function - lặp lại codeprint("=" * 40)print("Welcome")print("=" * 40) # Code khác... print("=" * 40)print("Goodbye")print("=" * 40) # Dùng function - reusabledef print_header(text): print("=" * 40) print(text) print("=" * 40) print_header("Welcome")# Code khác...print_header("Goodbye")
Định Nghĩa Function
Cú Pháp Cơ Bản
# Syntax:# def function_name(parameters):# """docstring"""# code block# return value # Simple function (no parameters, no return)def greet(): print("Hello, World!") # Call functiongreet() # Hello, World! # Function với parameterdef greet_person(name): print(f"Hello, {name}!") greet_person("Alice") # Hello, Alice!greet_person("Bob") # Hello, Bob! # Function với returndef add(a, b): result = a + b return result sum_result = add(5, 3)print(sum_result) # 8
Naming Convention
# ✅ Good - snake_casedef calculate_total(): pass def get_user_name(): pass def is_valid(): pass # ❌ Bad - không theo conventiondef CalculateTotal(): # PascalCase for classes pass def getUserName(): # camelCase for JavaScript pass
Parameters và Arguments
Parameter là biến trong function definition.
Argument là giá trị truyền vào khi call function.
def greet(name): # 'name' là parameter print(f"Hello, {name}!") greet("Alice") # "Alice" là argument
Positional Arguments
Positional arguments phải theo đúng thứ tự.
def describe_person(name, age, city): print(f"{name} is {age} years old and lives in {city}") # Đúng thứ tựdescribe_person("Alice", 25, "Hanoi")# Alice is 25 years old and lives in Hanoi # Sai thứ tự - lỗi logicdescribe_person(25, "Alice", "Hanoi")# 25 is Alice years old and lives in Hanoi (Sai!)
Keyword Arguments
Keyword arguments chỉ định tên parameter.
def describe_person(name, age, city): print(f"{name} is {age} years old and lives in {city}") # Keyword arguments - không cần đúng thứ tựdescribe_person(age=25, name="Alice", city="Hanoi")describe_person(city="HCMC", name="Bob", age=30) # Mix positional và keyworddescribe_person("Charlie", age=35, city="Danang") # ❌ Keyword trước positional - SyntaxError# describe_person(name="Alice", 25, "Hanoi")
Default Arguments
Default arguments có giá trị mặc định.
def greet(name, greeting="Hello"): print(f"{greeting}, {name}!") # Dùng defaultgreet("Alice") # Hello, Alice! # Override defaultgreet("Bob", "Hi") # Hi, Bob!greet("Charlie", greeting="Hey") # Hey, Charlie! # Multiple defaultsdef create_user(username, email, role="user", active=True): return { "username": username, "email": email, "role": role, "active": active } user1 = create_user("alice", "[email protected]")print(user1) # {..., "role": "user", "active": True} user2 = create_user("bob", "[email protected]", role="admin")print(user2) # {..., "role": "admin", "active": True}
⚠️ Mutable Default Arguments:
# ❌ NGUY HIỂM - Mutable defaultdef add_item(item, items=[]): # BAD! items.append(item) return items list1 = add_item("apple")list2 = add_item("banana") # Cùng list với list1!print(list1) # ['apple', 'banana']print(list2) # ['apple', 'banana'] - Unexpected! # ✅ ĐÚNG - Dùng Nonedef add_item(item, items=None): if items is None: items = [] items.append(item) return items list1 = add_item("apple")list2 = add_item("banana")print(list1) # ['apple']print(list2) # ['banana'] - Correct!
Return Values
return trả về giá trị từ function.
Return Cơ Bản
# Return một valuedef square(x): return x ** 2 result = square(5)print(result) # 25 # Return nhiều values (tuple)def get_user(): name = "Alice" age = 25 city = "Hanoi" return name, age, city name, age, city = get_user()print(name, age, city) # Alice 25 Hanoi # Không có return - trả về Nonedef greet(name): print(f"Hello, {name}!") result = greet("Alice") # Hello, Alice!print(result) # None
Early Return
# Early return với guard clausedef divide(a, b): if b == 0: return None # Early return return a / b print(divide(10, 2)) # 5.0print(divide(10, 0)) # None # Validate inputdef process_age(age): if age < 0: return "Invalid age" if age < 18: return "Minor" if age < 60: return "Adult" return "Senior" print(process_age(-5)) # Invalid ageprint(process_age(15)) # Minorprint(process_age(30)) # Adultprint(process_age(65)) # Senior
Return Complex Objects
# Return listdef get_evens(numbers): return [n for n in numbers if n % 2 == 0] evens = get_evens([1, 2, 3, 4, 5, 6])print(evens) # [2, 4, 6] # Return dictdef create_person(name, age, city): return { "name": name, "age": age, "city": city, "id": hash(name) } person = create_person("Alice", 25, "Hanoi")print(person) # Return function (advanced)def multiplier(factor): def multiply(x): return x * factor return multiply double = multiplier(2)triple = multiplier(3) print(double(5)) # 10print(triple(5)) # 15
Docstrings - Function Documentation
Docstring là documentation cho function.
# Single-line docstringdef greet(name): """Greet a person by name.""" print(f"Hello, {name}!") # Multi-line docstringdef calculate_area(width, height): """ Calculate the area of a rectangle. Parameters: width (float): The width of the rectangle height (float): The height of the rectangle Returns: float: The area of the rectangle """ return width * height # Access docstringprint(greet.__doc__)print(calculate_area.__doc__) # help() functionhelp(calculate_area)
Google Style Docstring:
def process_data(data, threshold=0.5): """ Process data with threshold filtering. Args: data (list): List of numbers to process threshold (float, optional): Minimum value threshold. Defaults to 0.5. Returns: list: Filtered and processed data Raises: ValueError: If data is empty Example: >>> process_data([0.3, 0.7, 0.9], threshold=0.5) [0.7, 0.9] """ if not data: raise ValueError("Data cannot be empty") return [x for x in data if x >= threshold]
Ví Dụ Thực Tế
1. Calculator Functions
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 # Use functionsprint(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(f"10 / 0 = {divide(10, 0)}")
2. Temperature Converter
def celsius_to_fahrenheit(celsius): """Convert Celsius to Fahrenheit.""" return (celsius * 9/5) + 32 def fahrenheit_to_celsius(fahrenheit): """Convert Fahrenheit to Celsius.""" return (fahrenheit - 32) * 5/9 def celsius_to_kelvin(celsius): """Convert Celsius to Kelvin.""" return celsius + 273.15 def kelvin_to_celsius(kelvin): """Convert Kelvin to Celsius.""" return kelvin - 273.15 # Testtemp_c = 25print(f"{temp_c}°C = {celsius_to_fahrenheit(temp_c):.1f}°F")print(f"{temp_c}°C = {celsius_to_kelvin(temp_c):.1f}K") temp_f = 77print(f"{temp_f}°F = {fahrenheit_to_celsius(temp_f):.1f}°C")
3. String Utilities
def is_palindrome(text): """ Check if text is a palindrome. Args: text (str): Text to check Returns: bool: True if palindrome, False otherwise """ text = text.lower().replace(" ", "") return text == text[::-1] def count_words(text): """Count words in text.""" return len(text.split()) def capitalize_words(text): """Capitalize first letter of each word.""" return " ".join(word.capitalize() for word in text.split()) def reverse_words(text): """Reverse order of words.""" return " ".join(reversed(text.split())) # Testprint(is_palindrome("radar")) # Trueprint(is_palindrome("hello")) # Falseprint(count_words("Hello world from Python")) # 4print(capitalize_words("hello world")) # Hello Worldprint(reverse_words("Hello World")) # World Hello
4. Validation Functions
def is_valid_email(email): """ Basic email validation. Args: email (str): Email to validate Returns: bool: True if valid format """ if "@" not in email: return False if email.count("@") != 1: return False if "." not in email.split("@")[1]: return False return True def is_valid_phone(phone): """ Validate phone number (10 digits). Args: phone (str): Phone number Returns: bool: True if valid """ phone = phone.replace("-", "").replace(" ", "") return phone.isdigit() and len(phone) == 10 def is_strong_password(password): """ Check password strength. Requirements: - At least 8 characters - Contains uppercase - Contains lowercase - Contains digit Args: password (str): Password to check Returns: tuple: (bool, str) - (is_valid, message) """ if len(password) < 8: return False, "Too short (min 8 chars)" has_upper = any(c.isupper() for c in password) has_lower = any(c.islower() for c in password) has_digit = any(c.isdigit() for c in password) if not has_upper: return False, "Need uppercase" if not has_lower: return False, "Need lowercase" if not has_digit: return False, "Need digit" return True, "Strong password" # Testprint(is_valid_email("[email protected]")) # Trueprint(is_valid_email("invalid.email")) # False print(is_valid_phone("0123456789")) # Trueprint(is_valid_phone("012-345-6789")) # True valid, msg = is_strong_password("Weak1")print(f"{valid}: {msg}")valid, msg = is_strong_password("StrongPass123")print(f"{valid}: {msg}")
5. List Operations
def find_max(numbers): """Find maximum value in list.""" if not numbers: return None max_val = numbers[0] for num in numbers: if num > max_val: max_val = num return max_val def find_min(numbers): """Find minimum value in list.""" if not numbers: return None min_val = numbers[0] for num in numbers: if num < min_val: min_val = num return min_val def calculate_average(numbers): """Calculate average of numbers.""" if not numbers: return None return sum(numbers) / len(numbers) def remove_duplicates(items): """Remove duplicates while preserving order.""" seen = set() result = [] for item in items: if item not in seen: seen.add(item) result.append(item) return result def chunk_list(items, size): """ Split list into chunks of given size. Args: items (list): List to chunk size (int): Chunk size Returns: list: List of chunks """ chunks = [] for i in range(0, len(items), size): chunks.append(items[i:i + size]) return chunks # Testnumbers = [3, 7, 1, 9, 2, 8, 5]print(f"Max: {find_max(numbers)}")print(f"Min: {find_min(numbers)}")print(f"Average: {calculate_average(numbers):.2f}") items = [1, 2, 2, 3, 4, 4, 5]print(f"Unique: {remove_duplicates(items)}") data = [1, 2, 3, 4, 5, 6, 7, 8, 9]print(f"Chunks: {chunk_list(data, 3)}")
Best Practices
# 1. Function nên làm một việc# ❌ Làm quá nhiều việcdef process_user(name, email): # Validate if "@" not in email: return False # Save to database save_to_db(name, email) # Send email send_welcome_email(email) # Log activity log_activity(name) # ✅ Chia thành nhiều functionsdef validate_email(email): return "@" in email def create_user(name, email): if not validate_email(email): return False save_to_db(name, email) send_welcome_email(email) log_activity(name) return True # 2. Descriptive names# ❌ Uncleardef calc(x, y): return x * y * 0.2 # ✅ Cleardef calculate_tax(price, quantity): TAX_RATE = 0.2 return price * quantity * TAX_RATE # 3. Avoid side effects# ❌ Modifies globaltotal = 0def add_to_total(value): global total total += value # ✅ Pure functiondef add_to_total(total, value): return total + value # 4. Keep it short# ✅ Max 20-30 lines per function
Bài Tập Thực Hành
Bài 1: Basic Functions
Viết functions:
greet(name, greeting="Hello")- Greet personis_even(n)- Check if evenfactorial(n)- Calculate factorial
Bài 2: String Functions
Viết functions:
count_vowels(text)- Count vowelsis_anagram(s1, s2)- Check if anagramsremove_spaces(text)- Remove all spaces
Bài 3: Math Functions
Viết functions:
is_prime(n)- Check prime numbergcd(a, b)- Greatest common divisorlcm(a, b)- Least common multiple
Bài 4: List Functions
Viết functions:
flatten(nested_list)- Flatten nested listrotate_list(lst, k)- Rotate list k positionsfind_pairs(numbers, target)- Find pairs summing to target
Bài 5: Grade Calculator
Viết function calculate_grade(scores):
- Input: list of scores
- Return: dict với average, grade (A-F), status (Pass/Fail)
Tóm Tắt
✅ Function: def name(parameters):
✅ Parameters: variables trong definition
✅ Arguments: values khi call function
✅ Return: trả về value với return
✅ Default arguments: giá trị mặc định
✅ Keyword arguments: chỉ định tên parameter
✅ Docstring: document function với """
Bài Tiếp Theo
Bài 11.2: Functions (Phần 2) - *args, **kwargs, lambda functions, scope, và recursion.
Remember:
- Function names: snake_case
- One function, one purpose
- Use descriptive names
- Document với docstrings
- Avoid mutable default arguments
- Keep functions short và focused!