Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

๐ŸŸข Beginner๐Ÿ”ต Advanced
Why Functions?Parameters & ArgumentsReturn StatementsScopeDefault ParametersVariable Arguments (*args)Lambda FunctionsDecoratorsFunctional ProgrammingBest Practices
Python/Functions/Best Practices

โœจ Best Practices โ€” Writing Clean, Efficient Functions

Write functions that are maintainable, efficient, and a joy to work with.


๐Ÿ“ Naming Conventions

Function Names Should Be Descriptive

# โŒ Bad โ€” unclear what it does
def process(x):
    return x * 2

# โœ… Good โ€” clear purpose
def double_number(x):
    return x * 2

# โœ… Better โ€” even more descriptive
def calculate_doubled_value(value):
    return value * 2

Use Verbs for Actions

# โœ… Good โ€” verbs indicate what function does
def get_user_name(user_id):
    pass

def validate_email(email):
    pass

def send_notification(message):
    pass

# โŒ Bad โ€” unclear action
def user_name(user_id):
    pass

def email(email):
    pass

๐Ÿ“š Documentation and Docstrings

Include Docstrings

def calculate_bmi(weight_kg, height_m):
    """
    Calculate Body Mass Index.

    Args:
        weight_kg (float): Weight in kilograms
        height_m (float): Height in meters

    Returns:
        float: BMI value

    Example:
        >>> calculate_bmi(70, 1.75)
        22.86
    """
    return weight_kg / (height_m ** 2)

Document Edge Cases

def divide_safe(a, b):
    """
    Divide two numbers safely.

    Args:
        a (float): Numerator
        b (float): Denominator

    Returns:
        float: Result of a / b, or None if division by zero

    Note:
        Returns None rather than raising exception to handle
        division by zero gracefully.
    """
    if b == 0:
        return None
    return a / b

โœ… Single Responsibility Principle

Each function should do ONE thing:

# โŒ Bad โ€” does too much
def process_user_data(user_id):
    user = fetch_user_from_db(user_id)
    user["age"] = 2024 - user["birth_year"]
    send_email_notification(user["email"])
    save_to_cache(user)
    log_activity(user_id)
    return user

# โœ… Good โ€” each function has one job
def get_user_with_age(user_id):
    user = fetch_user_from_db(user_id)
    user["age"] = 2024 - user["birth_year"]
    return user

def notify_user(user):
    send_email_notification(user["email"])

def cache_user(user):
    save_to_cache(user)

def log_access(user_id):
    log_activity(user_id)

# In main code
user = get_user_with_age(user_id)
notify_user(user)
cache_user(user)
log_access(user_id)

๐Ÿ”’ Keep Functions Small

Shorter functions are easier to test and debug:

# โŒ Bad โ€” 30 lines doing multiple things
def process_order(order_id):
    # Fetch order
    # Validate items
    # Calculate totals
    # Apply discounts
    # Check inventory
    # Process payment
    # Send confirmation
    # Update database
    # ... (24 more lines)

# โœ… Good โ€” small, focused functions
def validate_order(order):
    return all(item["quantity"] > 0 for item in order["items"])

def calculate_order_total(order):
    return sum(item["price"] * item["quantity"] for item in order["items"])

def apply_discount(total, discount_percent):
    return total * (1 - discount_percent / 100)

def process_order(order_id):
    order = fetch_order(order_id)
    if not validate_order(order):
        raise ValueError("Invalid order")
    total = calculate_order_total(order)
    total = apply_discount(total, order.get("discount", 0))
    process_payment(total)
    return order

๐Ÿ“ค Return Values Consistency

Always return the same type:

# โŒ Bad โ€” inconsistent return types
def find_user(name):
    if name:
        return {"name": name}  # dict
    else:
        return "Not found"  # string

# โœ… Good โ€” always returns dict or None
def find_user(name):
    if name:
        return {"name": name}
    return None

# โœ… Better โ€” explicit error handling
def find_user(name):
    if not name:
        raise ValueError("Name cannot be empty")
    return {"name": name}

โšก Performance Optimization

Cache Expensive Computations

from functools import lru_cache

# โŒ Bad โ€” recalculates every time
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

fibonacci(35)  # Takes 3+ seconds!

# โœ… Good โ€” caches results
@lru_cache(maxsize=128)
def fibonacci_cached(n):
    if n <= 1:
        return n
    return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)

fibonacci_cached(35)  # Instant!

Avoid Redundant Operations

# โŒ Bad โ€” calculates length multiple times
def process_list(items):
    result = []
    for i in range(len(items)):  # Length calculated each iteration
        if items[i] > 0:  # Accessing item again
            result.append(items[i] * 2)  # Accessing again
    return result

# โœ… Good โ€” cleaner and more efficient
def process_list(items):
    return [item * 2 for item in items if item > 0]

๐Ÿ›ก๏ธ Input Validation

Validate at function boundaries:

# โŒ Bad โ€” no validation
def divide(a, b):
    return a / b

# โœ… Good โ€” validates inputs
def divide(a, b):
    if not isinstance(a, (int, float)):
        raise TypeError("a must be numeric")
    if not isinstance(b, (int, float)):
        raise TypeError("b must be numeric")
    if b == 0:
        raise ValueError("Division by zero not allowed")
    return a / b

๐ŸŽฏ Default Arguments vs Optional

Use sensible defaults:

# โŒ Bad โ€” forces users to specify everything
def create_config(model, temperature, max_tokens, top_p, frequency_penalty):
    pass

# โœ… Good โ€” reasonable defaults
def create_config(
    model="GPT-4",
    temperature=0.7,
    max_tokens=1000,
    top_p=1.0,
    frequency_penalty=0.0
):
    pass

# Usage
config1 = create_config()  # All defaults
config2 = create_config(temperature=0.3)  # Override one

๐Ÿ”„ Avoid Side Effects

Functions shouldn't modify external state:

# โŒ Bad โ€” modifies global state
global_list = []

def add_item(item):
    global_list.append(item)  # Side effect!
    return len(global_list)

# โœ… Good โ€” pure function
def add_item(item, items=None):
    if items is None:
        items = []
    items = items + [item]  # Creates new list
    return items, len(items)

โŒ Common Anti-Patterns

Anti-Pattern 1: Flag Parameters

# โŒ Bad โ€” flag parameter makes function do two things
def process_data(data, save_to_file=False):
    result = transform(data)
    if save_to_file:
        write_file("output.txt", result)
    return result

# โœ… Good โ€” separate concerns
def process_data(data):
    return transform(data)

def process_and_save(data, filename):
    result = process_data(data)
    write_file(filename, result)
    return result

Anti-Pattern 2: Long Parameter Lists

# โŒ Bad โ€” too many parameters
def create_user(name, email, age, city, country, phone, address, zip_code):
    pass

# โœ… Good โ€” group related data
def create_user(name, email, details):
    # details = {age, city, country, phone, address, zip_code}
    pass

# Even better โ€” use dataclass
from dataclasses import dataclass

@dataclass
class UserDetails:
    age: int
    city: str
    country: str
    phone: str
    address: str
    zip_code: str

def create_user(name: str, email: str, details: UserDetails):
    pass

Anti-Pattern 3: Modifying Parameters

# โŒ Bad โ€” modifies input list
def add_prefix(prefix, items):
    for i in range(len(items)):
        items[i] = prefix + items[i]  # Modifies!
    return items

# โœ… Good โ€” returns new list
def add_prefix(prefix, items):
    return [prefix + item for item in items]

๐Ÿงช Testability

Write functions that are easy to test:

# โŒ Bad โ€” hard to test (depends on external systems)
def process_order(order_id):
    order = fetch_from_database(order_id)  # External dependency
    total = calculate_total(order)
    send_to_payment_api(total)  # External dependency
    return True

# โœ… Good โ€” pure logic, testable
def calculate_order_total(order):
    """Pure function โ€” no side effects."""
    return sum(item["price"] * item["qty"] for item in order["items"])

# Dependency injection โ€” pass dependencies in
def process_order(order_id, db_fetch, payment_api):
    order = db_fetch(order_id)
    total = calculate_order_total(order)
    payment_api.charge(total)
    return True

# Tests
def test_calculate_total():
    order = {"items": [{"price": 10, "qty": 2}]}
    assert calculate_order_total(order) == 20

๐Ÿ“‹ Error Handling

Choose appropriate error strategies:

# Strategy 1: Raise exceptions
def get_positive_number(n):
    if n <= 0:
        raise ValueError("Must be positive")
    return n

# Strategy 2: Return None
def safe_divide(a, b):
    if b == 0:
        return None
    return a / b

# Strategy 3: Return default
def get_config(key, default=None):
    config = load_config()
    return config.get(key, default)

# Choose based on context:
# - Raise: Program error, must be fixed
# - Return None: Optional operation, caller can handle
# - Default: Configuration, sensible fallback

๐Ÿ”‘ Quick Reference Checklist

โœ… Clear, descriptive name
โœ… Docstring with Args/Returns
โœ… Single responsibility
โœ… Small and focused
โœ… Consistent return types
โœ… Input validation
โœ… Sensible defaults
โœ… No side effects
โœ… Easy to test
โœ… Efficient algorithm
โœ… Handles edge cases
โœ… Good error messages

๐Ÿ“Š Summary Table

PracticeWhyExample
Descriptive namesClarity`calculate_total` not `calc`
DocstringsDocumentation"""Calculate total cost"""
Single responsibilityMaintainabilityOne job per function
Small sizeTestability<20 lines ideal
Pure functionsPredictabilityNo side effects
Input validationRobustnessCheck before processing
DefaultsUsabilitySensible fallback values
TestabilityQualityDependency injection

๐ŸŽฏ When You See Code

Ask yourself:

  • What does this function do?
  • Could I test it easily?
  • Does it have a side effect?
  • Is the name clear?
  • Is it doing one thing?
  • Can I refactor it?

๐ŸŽ“ Advanced Topics

Ready to level up? Explore:

  • **Design Patterns** โ€” Decorator, Strategy, Factory
  • **Type Hints** โ€” Full typing for better code
  • **Async Functions** โ€” Concurrent execution
  • **Testing Strategies** โ€” Unit tests, mocks, fixtures

๐Ÿ“š What You've Completed

The Functions topic now covers:

โœ… Why functions matter

โœ… Parameters and arguments

โœ… Return statements

โœ… Local vs global scope

โœ… Advanced features (*args, **kwargs, defaults)

โœ… Lambda functions

โœ… Decorators

โœ… Functional programming (map, filter, reduce)

โœ… Best practices and patterns

You're now a functions expert!


Ready to put it all together? Try comprehensive challenges or take the full quiz


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep โ€ข Build Real โ€ข Verify Skills โ€ข Launch Forward

Courses

PythonFastapiReactJSCloud

ยฉ 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn