Module 3 ยท Lesson 9

Defining Functions with def

๐Ÿ Pythonโฑ 14 min read๐Ÿ“– Core Concept

Functions: Reusable, Named Code Blocks

Functions are the foundation of maintainable code. They let you name a chunk of logic, reuse it across your program, and test it in isolation. Good programmers write small, focused functions that do one thing well.

def greet(name): """Say hello to someone.""" # Docstring โ€” always document! return f"Hello, {name}!" # Call the function message = greet("Alice") print(message) # Hello, Alice!

Parameters and Arguments

# Positional arguments โ€” order matters def add(a, b): return a + b add(3, 5) # 8 add(5, 3) # 8 โ€” same here, but for subtract(5,3) != subtract(3,5) # Default parameters โ€” provide fallback values def greet(name, greeting="Hello"): return f"{greeting}, {name}!" greet("Alice") # "Hello, Alice!" greet("Bob", "Good morning") # "Good morning, Bob!" greet(greeting="Hi", name="Carol") # Keyword arguments โ€” order-independent # Important: default values are evaluated ONCE at definition # Never use mutable defaults like lists! def bad(items=[]): # BUG: same list shared across calls! items.append(1) return items def good(items=None): # CORRECT: create fresh list each time if items is None: items = [] items.append(1) return items

*args and **kwargs

# *args โ€” collect extra positional args as a TUPLE def add_all(*numbers): return sum(numbers) add_all(1, 2, 3) # 6 add_all(1, 2, 3, 4, 5) # 15 add_all() # 0 # **kwargs โ€” collect extra keyword args as a DICT def print_info(**details): for key, value in details.items(): print(f"{key}: {value}") print_info(name="Alice", age=30, city="NYC") # Combining all parameter types (must be in this order) def combined(required, *args, keyword_only="default", **kwargs): print(required, args, keyword_only, kwargs) combined("must", 1, 2, 3, keyword_only="x", extra="y") # must (1, 2, 3) x {'extra': 'y'} # Unpacking into function calls def add(a, b, c): return a + b + c nums = [1, 2, 3] add(*nums) # Same as add(1, 2, 3) data = {"a": 1, "b": 2, "c": 3} add(**data) # Same as add(a=1, b=2, c=3)

Return Values

# Return single value def square(x): return x ** 2 # Return multiple values (as a tuple) def min_max(numbers): return min(numbers), max(numbers) low, high = min_max([3, 1, 4, 1, 5, 9]) print(low, high) # 1 9 # Early return for guard clauses def divide(a, b): if b == 0: return None # Early return โ€” don't proceed return a / b # Functions without return give None def say_hi(): print("hi") result = say_hi() # Prints "hi" print(result) # None

Scope: Local vs Global

x = 10 # Global def my_func(): y = 20 # Local โ€” only visible inside print(x) # Can READ global x print(y) # Can read local y my_func() print(x) # 10 โ€” accessible # print(y) # NameError โ€” y doesn't exist outside the function # To MODIFY a global inside a function count = 0 def increment(): global count # Declare intent to modify global count += 1 # Without 'global', this is UnboundLocalError # nonlocal โ€” modify variable in enclosing function scope def outer(): total = 0 def inner(): nonlocal total total += 1 inner() return total

Docstrings

def calculate_bmi(weight_kg: float, height_m: float) -> float: """ Calculate Body Mass Index (BMI). Args: weight_kg: Weight in kilograms. height_m: Height in meters. Returns: BMI value rounded to 1 decimal place. Example: >>> calculate_bmi(70, 1.75) 22.9 """ return round(weight_kg / height_m ** 2, 1) # View docstring help(calculate_bmi) print(calculate_bmi.__doc__)

Key Takeaways

Practice Exercises

  1. Write celsius_to_fahrenheit(c) and fahrenheit_to_celsius(f). Test both.
  2. Write a function word_count(text) that returns a dict of word frequencies.
  3. Write a function that accepts any number of numbers and returns (min, max, average, sum).
  4. Write a function with proper docstring and type hints. Run help(your_function).
โ† List Comprehensions