Functions
Learn how to create reusable functions with parameters, return values, and local scope.
A function is a reusable block of code that performs a specific task. Functions help divide a large program into smaller, clearer, and reusable parts.
Python provides built-in functions such as print(), input(), len(), and sum(). You can also create your own functions. These are called user-defined functions.
Why Use Functions?
Functions help you:
- Reuse code instead of writing it repeatedly
- Divide a large problem into smaller parts
- Give meaningful names to operations
- Make programs easier to read and test
- Reduce duplication and maintenance effort
For example, instead of writing the same welcome message multiple times:
print("Welcome to OSDC.")
print("Welcome to OSDC.")
print("Welcome to OSDC.")
Create a function and call it whenever the message is needed:
def show_welcome_message():
print("Welcome to OSDC.")
show_welcome_message()
show_welcome_message()
show_welcome_message()
Defining a Function
Use the def keyword to define a function.
def function_name():
# statements belonging to the function
pass
A function definition contains:
- The
defkeyword - The function name
- Parentheses
() - A colon
: - An indented function body
def show_club_name():
print("OSDC")
Defining a function does not execute its body. The function runs only when it is called.
Calling a Function
Call a function by writing its name followed by parentheses.
def show_club_name():
print("OSDC")
show_club_name()
A function can be called multiple times:
def show_workshop_topic():
print("Python and FastAPI")
show_workshop_topic()
show_workshop_topic()
Function Execution Order
Python executes a program from top to bottom. The function must be defined before it is called.
def display_message():
print("OSDC workshop")
print("Before the call")
display_message()
print("After the call")
Output:
Before the call
OSDC workshop
After the call
Parameters and Arguments
A parameter is a variable listed in a function definition. An argument is the value passed to the function when it is called.
def greet_club(club_name): # club_name is a parameter
print(f"Welcome to {club_name}.")
greet_club("OSDC") # "OSDC" is an argument
A function can have multiple parameters:
def describe_workshop(workshop_name, topic):
print(f"Workshop: {workshop_name}")
print(f"Topic: {topic}")
describe_workshop("Full Stack Workshop", "HTML, CSS, JavaScript, Python, and FastAPI")
Arguments are matched with parameters according to their position when positional arguments are used.
def show_workshop(name, duration):
print(f"{name} lasts {duration} days.")
show_workshop("Python Workshop", 2)
Positional Arguments
With positional arguments, values are assigned to parameters in the order in which they are passed.
def register_workshop(workshop_name, seat_count):
print(f"Workshop: {workshop_name}")
print(f"Seats: {seat_count}")
register_workshop("FastAPI Workshop", 40)
The following call reverses the values and produces incorrect meaning, even though Python accepts it:
# register_workshop(40, "FastAPI Workshop")
Use the correct order or keyword arguments when the meaning needs to be especially clear.
Keyword Arguments
Keyword arguments are passed using parameter names.
def register_workshop(workshop_name, seat_count):
print(f"Workshop: {workshop_name}")
print(f"Seats: {seat_count}")
register_workshop(
seat_count=40,
workshop_name="FastAPI Workshop"
)
Keyword arguments can be passed in a different order from the function definition.
Positional arguments must come before keyword arguments:
def describe_workshop(name, topic, duration):
print(f"{name}: {topic} ({duration} days)")
describe_workshop("Python Workshop", topic="Python", duration=2)
Returning Values
The return statement sends a value back to the code that called the function.
def add_numbers(first_number, second_number):
return first_number + second_number
result = add_numbers(10, 20)
print(result)
A returned value can be stored, printed, or used in another expression:
def calculate_total_attendance(first_day, second_day):
return first_day + second_day
total = calculate_total_attendance(40, 35)
average = total / 2
print("Total attendance:", total)
print("Average attendance:", average)
return Stops a Function
When Python reaches return, the function stops immediately.
def check_seats(seat_count):
if seat_count <= 0:
return "Workshop full"
return "Seats available"
print(check_seats(10))
print(check_seats(0))
Code written after an unconditional return is unreachable and will not run.
Returning Multiple Values
A function can return multiple values. Python returns them as a tuple.
def get_workshop_details():
return "Python Workshop", 2
name, duration = get_workshop_details()
print(name)
print(duration)
A function can also return a dictionary when named fields make the result clearer:
def get_club_details():
return {
"name": "OSDC",
"institution": "JIIT, Noida",
"focus": "Open Source Development"
}
club = get_club_details()
print(club["name"])
Functions Without return
If a function does not explicitly return a value, it returns None.
def show_message():
print("Welcome to OSDC.")
result = show_message()
print(result)
# None
Printing a value and returning a value are different actions:
def print_member_count(member_count):
print(member_count)
def get_member_count(member_count):
return member_count
printed_value = print_member_count(50)
returned_value = get_member_count(50)
print(printed_value) # None
print(returned_value) # 50
Use return when the calling code needs to work with the result.
Default Parameters
A default parameter has a value that is used when the caller does not provide an argument.
def greet_member(member_name="OSDC member"):
print(f"Welcome, {member_name}.")
greet_member("Workshop participant")
greet_member()
Parameters with defaults must come after parameters without defaults.
def create_workshop(workshop_name, duration=2):
print(f"{workshop_name} lasts {duration} days.")
create_workshop("Python Workshop")
create_workshop("FastAPI Workshop", 3)
This definition is invalid because a required parameter follows a default parameter:
# def create_workshop(duration=2, workshop_name):
# pass
Avoid Mutable Default Values
Do not use a list or dictionary as a default parameter when the function will modify it.
Avoid this:
def add_topic(topic, topics=[]):
topics.append(topic)
return topics
Use None and create a new list inside the function:
def add_topic(topic, topics=None):
if topics is None:
topics = []
topics.append(topic)
return topics
print(add_topic("Python"))
print(add_topic("FastAPI"))
Local and Global Scope
The scope of a variable is the part of the program where that variable can be accessed.
A variable created inside a function is local to that function.
def show_workshop():
workshop_name = "Python Workshop"
print(workshop_name)
show_workshop()
# print(workshop_name) # NameError
A variable created outside a function is in the global scope.
club_name = "OSDC"
def show_club_name():
print(club_name)
show_club_name()
A function can read a global variable, but it is usually better to pass data as a parameter.
def show_club_name(club_name):
print(club_name)
show_club_name("OSDC")
Local Variables with the Same Name
A local variable can have the same name as a global variable. The local variable is used inside the function.
club_name = "OSDC"
def display_club():
club_name = "OSDC Development Team"
print(club_name)
display_club()
print(club_name)
Avoid relying heavily on global variables because they make functions harder to test and reuse.
Functions with Mutable Arguments
Lists and dictionaries are mutable. If a function modifies a mutable object passed to it, the change is visible outside the function.
def add_workshop(workshops, workshop_name):
workshops.append(workshop_name)
workshops = ["HTML and CSS"]
add_workshop(workshops, "Python and FastAPI")
print(workshops)
If you do not want the original list to change, pass a copy or create one inside the function.
def add_workshop_without_changing_original(workshops, workshop_name):
updated_workshops = workshops.copy()
updated_workshops.append(workshop_name)
return updated_workshops
workshops = ["HTML and CSS"]
updated_workshops = add_workshop_without_changing_original(
workshops,
"Python and FastAPI"
)
print(workshops)
print(updated_workshops)
Arbitrary Positional Arguments: *args
Use *args when a function should accept any number of positional arguments. Inside the function, args is a tuple.
def show_topics(*topics):
for topic in topics:
print(topic)
show_topics("HTML", "CSS", "JavaScript", "Python", "FastAPI")
The name args is a convention. The important part is the *.
def calculate_total(*attendance_values):
return sum(attendance_values)
print(calculate_total(40, 35, 50))
Regular parameters can come before *args:
def show_workshop_topics(workshop_name, *topics):
print(f"Workshop: {workshop_name}")
for topic in topics:
print(f"- {topic}")
show_workshop_topics(
"Full Stack Workshop",
"HTML",
"CSS",
"JavaScript",
"Python",
"FastAPI"
)
Arbitrary Keyword Arguments: **kwargs
Use **kwargs when a function should accept any number of keyword arguments. Inside the function, kwargs is a dictionary.
def show_club_details(**details):
for key, value in details.items():
print(f"{key}: {value}")
show_club_details(
name="OSDC",
institution="JIIT, Noida",
focus="Open Source Development"
)
The name kwargs is a convention. The important part is the **.
Regular parameters and **kwargs can be combined:
def create_workshop(workshop_name, **details):
print(f"Workshop: {workshop_name}")
for key, value in details.items():
print(f"{key}: {value}")
create_workshop(
"Python Workshop",
duration=2,
level="Beginner",
organiser="OSDC"
)
Keyword-Only Parameters
Parameters after * must be passed by keyword.
def register_workshop(workshop_name, *, seat_count, is_online):
print(workshop_name)
print(seat_count)
print(is_online)
register_workshop(
"FastAPI Workshop",
seat_count=40,
is_online=False
)
This prevents unclear positional calls such as register_workshop("FastAPI Workshop", 40, False).
Positional-Only Parameters
Parameters before / can be passed only positionally. This syntax is available in Python 3.8 and later.
def calculate_total(first_value, second_value, /):
return first_value + second_value
print(calculate_total(10, 20))
This call is invalid because the parameters are positional-only:
# calculate_total(first_value=10, second_value=20)
Most beginner functions do not need positional-only parameters, but they are useful when designing strict public APIs.
Function Annotations
Type hints can document the expected types of parameters and the return value.
def calculate_total(first_value: int, second_value: int) -> int:
return first_value + second_value
print(calculate_total(10, 20))
Python does not enforce these annotations automatically. Type checkers and code editors can use them to identify possible mistakes.
With collections:
def get_topics() -> list[str]:
return ["HTML", "CSS", "JavaScript", "Python", "FastAPI"]
def count_workshops(workshops: list[str]) -> int:
return len(workshops)
workshops = get_topics()
print(count_workshops(workshops))
Docstrings
A docstring describes what a function does. It is written as the first statement inside the function body.
def calculate_average(first_value, second_value):
"""Return the average of two numeric values."""
return (first_value + second_value) / 2
print(calculate_average(40, 50))
You can access a function’s docstring using __doc__:
print(calculate_average.__doc__)
A useful docstring explains the function’s purpose, parameters, and return value when the function is more complex.
def get_available_seats(total_seats, registered_members):
"""Return the number of seats that are still available."""
return total_seats - registered_members
Functions Calling Other Functions
Functions can call other functions to divide a task into smaller steps.
def calculate_available_seats(total_seats, registered_members):
return total_seats - registered_members
def show_registration_status(total_seats, registered_members):
available_seats = calculate_available_seats(
total_seats,
registered_members
)
if available_seats > 0:
print(f"{available_seats} seats are available.")
else:
print("The workshop is full.")
show_registration_status(50, 42)
This keeps each function focused on one responsibility.
Functions with Input
Input can be collected inside a function, and the result can be returned to the caller.
def get_member_count():
while True:
try:
member_count = int(input("Enter the OSDC member count: "))
if member_count >= 0:
return member_count
print("Member count cannot be negative.")
except ValueError:
print("Please enter a whole number.")
member_count = get_member_count()
print(f"OSDC member count: {member_count}")
A function can also receive input as a parameter. This makes the function easier to test because it does not depend on interactive input.
def describe_workshop(workshop_name, seat_count):
return f"{workshop_name} has {seat_count} seats."
workshop_name = input("Workshop name: ").strip()
seat_count = int(input("Number of seats: "))
print(describe_workshop(workshop_name, seat_count))
Recursive Functions
A recursive function calls itself. Every recursive function needs:
- A base case that stops the recursion
- A recursive case that moves toward the base case
def countdown(number):
if number == 0:
print("Start!")
return
print(number)
countdown(number - 1)
countdown(3)
Output:
3
2
1
Start!
Without a base case, the function would continue calling itself until Python raises a RecursionError.
A recursive factorial function:
def factorial(number):
if number == 0 or number == 1:
return 1
return number * factorial(number - 1)
print(factorial(5))
Loops are often simpler and more efficient for basic repetition. Recursion is useful when a problem naturally contains smaller versions of itself, such as traversing nested structures.
Lambda Functions
A lambda is a small anonymous function containing one expression.
square = lambda number: number ** 2
print(square(5))
A regular function is usually clearer when the operation is reused or contains multiple statements:
def square(number):
return number ** 2
Lambda functions are commonly used with functions such as sorted():
workshops = [
{"name": "Python", "attendance": 45},
{"name": "FastAPI", "attendance": 30},
{"name": "JavaScript", "attendance": 50}
]
sorted_workshops = sorted(
workshops,
key=lambda workshop: workshop["attendance"]
)
print(sorted_workshops)
Functions as Values
Functions can be stored in variables and passed to other functions.
def show_python():
print("Python workshop")
selected_action = show_python
selected_action()
A function that accepts another function is called a higher-order function.
def run_action(action):
action()
def show_osdc_message():
print("Welcome to OSDC.")
run_action(show_osdc_message)
A Function-Based Menu
Functions are useful for organizing a menu-driven program.
def show_workshops():
print("Available workshops:")
print("- HTML and CSS")
print("- JavaScript")
print("- Python")
print("- FastAPI")
def show_club_information():
print("OSDC")
print("Open Source Developers Community")
print("JIIT, Noida")
def show_menu():
print("\nOSDC Menu")
print("1. View workshops")
print("2. View club information")
print("3. Exit")
while True:
show_menu()
choice = input("Enter your choice: ").strip()
if choice == "1":
show_workshops()
elif choice == "2":
show_club_information()
elif choice == "3":
print("Goodbye.")
break
else:
print("Invalid choice.")
Common Mistakes
Defining but Not Calling a Function
def show_message():
print("Welcome to OSDC.")
# The function does not run until it is called.
show_message()
Forgetting to Return a Value
def add_numbers(first_number, second_number):
first_number + second_number # The result is not returned
result = add_numbers(10, 20)
print(result) # None
Correct version:
def add_numbers(first_number, second_number):
return first_number + second_number
Using the Wrong Number of Arguments
def show_workshop(name, duration):
print(f"{name}: {duration} days")
# show_workshop("Python Workshop") # TypeError
show_workshop("Python Workshop", 2)
Mutable Default Arguments
Use None instead of a list or dictionary as a default value when the function modifies it.
def add_topic(topic, topics=None):
if topics is None:
topics = []
topics.append(topic)
return topics
Overusing Global Variables
Pass data as parameters and return results instead of changing global variables from inside functions.
def add_member(member_count):
return member_count + 1
member_count = 50
member_count = add_member(member_count)
print(member_count)
Quick Reference
def function_name(parameter):
return parameter
result = function_name(argument)
def function_name(parameter, optional_parameter=default_value):
return parameter
def function_name(*args, **kwargs):
pass
| Concept | Meaning |
|---|---|
def |
Starts a function definition |
| Parameter | Variable in a function definition |
| Argument | Value passed during a function call |
return |
Sends a value back to the caller |
| Default parameter | Value used when an argument is omitted |
*args |
Accepts extra positional arguments |
**kwargs |
Accepts extra keyword arguments |
| Docstring | Description written inside a function |
| Local variable | Variable available inside its function |
| Global variable | Variable defined outside functions |
