🔧 Functions Basics¶
Functions are reusable blocks of code. Write once, use many times.
Why Functions?¶
Without functions:
# Calculate area of rectangle 1
length1 = 5
width1 = 3
area1 = length1 * width1
print(f"Area: {area1}")
# Calculate area of rectangle 2 (copy-paste, error-prone!)
length2 = 8
width2 = 4
area2 = length2 * width2
print(f"Area: {area2}")
With functions:
def calculate_area(length, width):
return length * width
area1 = calculate_area(5, 3)
area2 = calculate_area(8, 4)
Defining Functions¶
Basic syntax:
def function_name(parameters):
"""Docstring: describes what the function does"""
# Function body
return result # Optional
Examples:¶
# No parameters, no return
def say_hello():
print("Hello!")
# With parameters, with return
def add(a, b):
return a + b
# With docstring
def greet(name):
"""Greet a person by name."""
return f"Hello, {name}!"
Calling Functions¶
# Define
def greet(name):
return f"Hello, {name}!"
# Call
message = greet("Alice") # "Hello, Alice!"
print(greet("Bob")) # Prints: Hello, Bob!
# Can call multiple times
for person in ["Alice", "Bob", "Charlie"]:
print(greet(person))
Parameters vs Arguments¶
- Parameters: Variables in the function definition
- Arguments: Actual values passed when calling
Return Values¶
Single Return:¶
Multiple Returns:¶
def divide_with_remainder(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder # Returns a tuple
q, r = divide_with_remainder(17, 5) # q=3, r=2
No Return (returns None):¶
def print_greeting(name):
print(f"Hello, {name}!")
# No return statement
result = print_greeting("Alice") # Prints, but...
print(result) # None
Early Return:¶
def get_grade(score):
if score < 0 or score > 100:
return "Invalid score" # Exit early
if score >= 90:
return "A"
elif score >= 80:
return "B"
# etc.
Default Parameters¶
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Alice", "Hi")) # Hi, Alice!
print(greet("Alice", "Howdy")) # Howdy, Alice!
Rules for Defaults:¶
# ✅ Correct: defaults come after non-defaults
def func(required, optional="default"):
pass
# ❌ Wrong: non-default after default
def func(optional="default", required): # SyntaxError
pass
Keyword Arguments¶
Call with parameter names for clarity:
def create_user(name, age, city):
return f"{name}, {age}, from {city}"
# Positional arguments (order matters)
create_user("Alice", 25, "NYC")
# Keyword arguments (order doesn't matter)
create_user(city="NYC", name="Alice", age=25)
# Mix (positional first, then keyword)
create_user("Alice", city="NYC", age=25)
*args and **kwargs¶
*args - Variable Positional Arguments:¶
def add_all(*args):
"""Add any number of arguments."""
total = 0
for num in args:
total += num
return total
add_all(1, 2) # 3
add_all(1, 2, 3, 4, 5) # 15
**kwargs - Variable Keyword Arguments:¶
def print_info(**kwargs):
"""Print any key-value pairs."""
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="NYC")
# name: Alice
# age: 25
# city: NYC
Combined:¶
def func(required, *args, default="value", **kwargs):
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Default: {default}")
print(f"Kwargs: {kwargs}")
func(1, 2, 3, default="custom", extra="data")
Scope¶
Variables have scope — where they can be accessed.
Local Scope:¶
def my_function():
x = 10 # Local to this function
print(x)
my_function()
print(x) # ❌ NameError: x is not defined
Global Scope:¶
x = 10 # Global variable
def my_function():
print(x) # Can read global
my_function() # Prints 10
print(x) # Also 10
Modifying Global (avoid if possible):¶
count = 0
def increment():
global count # Declare intention to modify global
count += 1
increment()
print(count) # 1
Lambda Functions¶
Anonymous (unnamed) functions for simple operations:
# Regular function
def square(x):
return x ** 2
# Lambda equivalent
square = lambda x: x ** 2
# Both work the same
print(square(5)) # 25
Common Usage:¶
# Sorting with custom key
people = [("Alice", 25), ("Bob", 30), ("Charlie", 20)]
sorted_by_age = sorted(people, key=lambda p: p[1])
# With map/filter
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
Docstrings¶
Document your functions:
def calculate_bmi(weight, height):
"""
Calculate Body Mass Index.
Args:
weight: Weight in kilograms
height: Height in meters
Returns:
BMI as a float
Example:
>>> calculate_bmi(70, 1.75)
22.86
"""
return weight / (height ** 2)
# Access docstring
print(calculate_bmi.__doc__)
help(calculate_bmi)
Quick Reference¶
# Basic function
def function_name(param1, param2):
return result
# Default parameter
def func(param="default"):
pass
# Variable args
def func(*args, **kwargs):
pass
# Lambda
lambda x: x * 2
# Docstring
def func():
"""Description of function."""
pass
Next Steps¶
Practice with examples.py, then try exercises.py!