- Function anatomy — the structure of a Python function
- The def keyword — how to start defining a function
- Function naming — rules and best practices
- Parameters — inputs to your functions
- Docstrings — documenting your functions
- Function body — the logic that does the work
- Return statement — outputting results
Anatomy of a User-Defined Function
A user-defined function in Python is composed of several essential elements that work together to create reusable, organized code. Understanding each element is crucial for writing effective functions.
💡 Key concept: Every Python function follows a specific structure. Think of it like a recipe — it has a name, ingredients (parameters), instructions (body), and a result (return value).
The def Keyword
Starting a Function Definition
# The def keyword is used to define a function
# Syntax: def function_name(parameters):
# The colon (:) indicates the start of the function body
# Example 1: Simple function definition
def greet():
"""Print a greeting"""
print("Hello, World!")
# Example 2: Function with parameters
def greet_person(name):
"""Greet a specific person"""
print(f"Hello, {name}!")
# Example 3: Function with return value
def add(a, b):
"""Add two numbers and return the result"""
return a + b
# Key points about def:
# 1. Must be followed by a space and the function name
# 2. Parentheses () are required even if no parameters
# 3. A colon : ends the first line
# 4. The body must be indented (4 spaces recommended)
def keyword essentials:
- Always used — every user-defined function starts with def
- Followed by name — the function name comes next
- Parentheses required — even with no parameters
- Colon needed — indicates the function body begins
- Indentation matters — body must be indented
Quick Check: What keyword is used to define a function? (Answer: def)
Function Name
Naming Your Functions
# Function names follow the same rules as variable names
# Rules for valid function names:
# 1. Must start with a letter (a-z, A-Z) or underscore (_)
# 2. Can contain letters, numbers, and underscores
# 3. Cannot start with a number
# 4. Cannot be a Python keyword (if, for, while, etc.)
# ✅ Valid function names
def calculate_area():
pass
def process_data():
pass
def get_user_info():
pass
def _private_helper():
pass
# ❌ Invalid function names
# def 123_invalid(): # Cannot start with number
# def if(): # Cannot use keyword
# def my-function(): # Cannot use hyphen
# Best practices for naming:
# 1. Use descriptive names (what does the function do?)
# 2. Use snake_case (lowercase with underscores)
# 3. Start with a verb (get, set, calculate, process)
# 4. Be consistent in your naming
# Good examples:
def calculate_total_price(items):
"""Calculate the total price of items"""
pass
def find_maximum(numbers):
"""Find the maximum value in a list"""
pass
def is_valid_email(email):
"""Check if an email is valid"""
pass
# Bad examples:
def calc(items): # Too vague
pass
def func1(): # Meaningless name
pass
Naming best practices:
- Be descriptive — name should say what the function does
- Use snake_case — lowercase with underscores
- Start with a verb — get, set, calculate, process
- Be consistent — use the same naming style throughout
- Avoid abbreviations — unless they're well-known
Quick Check: Which naming style is recommended for Python functions? (Answer: snake_case)
Parameters
Inputs to Your Function
# Parameters are placeholders for values passed to the function
# 1. Function with no parameters
def say_hello():
print("Hello!")
# 2. Function with one parameter
def greet(name):
print(f"Hello, {name}!")
# 3. Function with multiple parameters
def calculate_total(price, quantity, tax_rate=0.10):
"""Calculate total cost with tax"""
subtotal = price * quantity
tax = subtotal * tax_rate
return subtotal + tax
# 4. Function with default parameters
def greet_user(name="Guest", greeting="Hello"):
"""Greet a user with customizable greeting"""
return f"{greeting}, {name}!"
# 5. Function with variable arguments (*args)
def sum_all(*args):
"""Sum any number of arguments"""
return sum(args)
# 6. Function with keyword arguments (**kwargs)
def print_user_info(**kwargs):
"""Print user information"""
for key, value in kwargs.items():
print(f"{key}: {value}")
# 7. Function with positional-only arguments (Python 3.8+)
def greet_person(name, /, greeting="Hello"):
"""name must be passed positionally"""
return f"{greeting}, {name}!"
# 8. Function with keyword-only arguments (Python 3.8+)
def greet_person(*, name, greeting="Hello"):
"""name and greeting must be passed as keywords"""
return f"{greeting}, {name}!"
Parameter types:
- Required parameters — must be provided when calling
- Default parameters — have default values if not provided
- *args — variable number of positional arguments
- **kwargs — variable number of keyword arguments
- Positional-only — must be passed by position
- Keyword-only — must be passed by name
Quick Check: What is the difference between a parameter and an argument? (Answer: Parameters are defined in the function; arguments are passed when calling)
Docstrings
Documenting Your Functions
# Docstrings are strings that document what a function does
# They are written as the first line of the function body
# 1. Simple docstring
def greet(name):
"""Greet a person by name."""
return f"Hello, {name}!"
# 2. Multi-line docstring
def calculate_area(length, width):
"""
Calculate the area of a rectangle.
Args:
length (float): The length of the rectangle
width (float): The width of the rectangle
Returns:
float: The area of the rectangle
Example:
>>> calculate_area(5, 3)
15
"""
return length * width
# 3. Docstring with type hints
def multiply(a: int, b: int) -> int:
"""
Multiply two integers.
This function takes two integers and returns their product.
Parameters:
-----------
a : int
The first number
b : int
The second number
Returns:
--------
int
The product of a and b
Examples:
---------
>>> multiply(2, 3)
6
>>> multiply(-1, 5)
-5
"""
return a * b
# 4. Accessing docstrings
print(greet.__doc__) # Prints the docstring
help(greet) # Shows help information
# 5. Google-style docstring
def process_data(data, clean=True):
"""
Process data with optional cleaning.
Args:
data (list): List of data items to process
clean (bool): Whether to clean data before processing
Returns:
dict: Processed results
"""
pass
# 6. Numpy-style docstring
def calculate_statistics(data):
"""
Calculate basic statistics for a dataset.
Parameters
----------
data : array-like
Input data for statistical analysis
Returns
-------
dict
Dictionary containing mean, median, and standard deviation
Raises
------
ValueError
If data is empty
"""
pass
Docstring best practices:
- Always include — every function should have a docstring
- Describe purpose — explain what the function does
- Document parameters — list each parameter with description
- Document return value — explain what is returned
- Include examples — show how to use the function
- Follow a style — PEP 257, Google, or Numpy style
Quick Check: What is the purpose of a docstring? (Answer: To document and describe what the function does)
Function Body
The Logic Inside Your Function
# The function body contains the code that does the work
# It must be indented (4 spaces recommended)
# 1. Simple function body
def greet(name):
"""Greet a person"""
message = f"Hello, {name}!" # Body starts here
return message
# 2. Multi-line function body
def calculate_average(scores):
"""Calculate the average of scores"""
# Body with multiple statements
if not scores:
return 0
total = 0
count = 0
for score in scores:
total += score
count += 1
average = total / count
return average
# 3. Function body with conditionals
def validate_age(age):
"""Validate that age is reasonable"""
if age < 0:
return "Invalid: Age cannot be negative"
elif age < 18:
return "Invalid: Must be 18 or older"
elif age > 120:
return "Invalid: Age seems too old"
else:
return "Valid"
# 4. Function body with loops
def find_primes(limit):
"""Find all prime numbers up to a limit"""
primes = []
for num in range(2, limit + 1):
is_prime = True
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
primes.append(num)
return primes
# 5. Function body with exception handling
def safe_divide(a, b):
"""Divide two numbers safely"""
try:
result = a / b
return result
except ZeroDivisionError:
return "Error: Division by zero"
except TypeError:
return "Error: Invalid input types"
# 6. Empty function body
def placeholder():
"""Placeholder function - to be implemented later"""
pass # pass is used for empty bodies
Function body best practices:
- Keep it focused — one responsibility per function
- Use meaningful variable names — inside the body
- Handle errors — use try/except where appropriate
- Avoid side effects — limit changes to external state
- Keep it readable — use comments and whitespace
- Single return point — or early returns for clarity
Quick Check: What is the purpose of the function body? (Answer: To contain the logic that performs the function's task)
Return Statement
Getting Results from Your Function
# The return statement sends a value back to the caller
# 1. Function with a single return value
def add(a, b):
"""Add two numbers"""
return a + b
result = add(5, 3)
print(result) # 8
# 2. Function with multiple return values
def get_user_info():
"""Return multiple values as a tuple"""
return "Alice", 25, "Engineer"
name, age, job = get_user_info()
print(name, age, job) # Alice 25 Engineer
# 3. Function with early return
def validate_age(age):
"""Validate age with early returns"""
if age < 0:
return "Invalid: Negative age"
if age < 18:
return "Invalid: Too young"
if age > 120:
return "Invalid: Too old"
return "Valid"
print(validate_age(25)) # Valid
print(validate_age(-5)) # Invalid: Negative age
# 4. Function with no return (returns None)
def print_message(msg):
"""Print a message - no return"""
print(msg)
# No return statement → returns None
result = print_message("Hello")
print(result) # None
# 5. Function with conditional return
def get_discount(price, member_level):
"""Calculate discount based on membership"""
if member_level == "gold":
return price * 0.20
elif member_level == "silver":
return price * 0.10
elif member_level == "bronze":
return price * 0.05
return 0 # No discount
# 6. Function returning different types
def process_data(data, operation):
"""Process data with different operations"""
if operation == "sum":
return sum(data)
elif operation == "avg":
return sum(data) / len(data) if data else 0
elif operation == "max":
return max(data) if data else None
elif operation == "min":
return min(data) if data else None
else:
return "Invalid operation"
# 7. Function returning a function (closure)
def create_multiplier(factor):
"""Create a function that multiplies by a factor"""
def multiplier(x):
return x * factor
return multiplier
double = create_multiplier(2)
print(double(5)) # 10
Return statement key points:
- return — sends a value back to the caller
- Multiple values — return as tuple
- No return — returns None
- Early return — exit function early
- Returns any type — int, str, list, dict, function
- Stops execution — code after return doesn't run
Quick Check: What does a function return if there is no return statement? (Answer: None)
Try It Yourself
Practice creating functions with all the elements you've learned. Modify the code and see what happens.
ELEMENTS OF USER-DEFINED FUNCTIONS
========================================
1. COMPLETE FUNCTION WITH ALL ELEMENTS
Original: $100, 20% off: $88.00
Original: $50, 10% off: $49.50
2. FUNCTION WITH MULTIPLE RETURN VALUES
Product: Laptop
Price: $999.99
Quantity: 2
Total: $1999.98
3. FUNCTION WITH DOCSTRING AND TYPE HINTS
'user@example.com' is valid: True
'invalid-email' is valid: False
4. FUNCTION WITH EARLY RETURN
Age 16: Minor - Not eligible
Age 25, Member: Adult - Member
Age 30, Non-member: Adult - Non-member
Elements of user-defined functions practice complete!
You've Got It!
You now understand all the essential elements of user-defined functions in Python — from the def keyword and function naming to parameters, docstrings, the function body, and return statements.
Quick Quiz
Test what you've learned:
Frequently Asked Questions
What is the difference between a parameter and an argument?
What is the difference between *args and **kwargs?
*args allows you to pass a variable number of positional arguments, which are collected as a tuple. **kwargs allows you to pass a variable number of keyword arguments, which are collected as a dictionary.
Why should I use docstrings?
Can a function return multiple values?
What's a common interview question about function elements?
What happens if I don't include a return statement?
None by default. This is a common behavior in Python.
Where to Go From Here
Now that you understand the elements of user-defined functions, check out these related topics:
Function Arguments
Master different types of function arguments.
Learn More →Nesting of Functions
Learn about inner functions and closures.
Learn More →Recursion
Learn about functions that call themselves.
Learn More →