Complete Guide to Python Functions (2026)
In Python programming, functions are reusable blocks of code that perform specific tasks. Instead of writing the same logic multiple times, you define a function once and call it whenever needed. Functions make your code modular, easier to debug, and follow the DRY (Don't Repeat Yourself) principle.
In this comprehensive guide, we'll explore everything about Python functions including their types, parameters vs arguments, return values, default arguments, keyword arguments, *args, **kwargs, best practices, and common mistakes.
๐ Table of Contents
๐น What is a Function in Python?
A function is a reusable block of code that performs a specific task. Functions help you break complex problems into smaller, manageable pieces. They also allow you to reuse code without duplication.
Think of a function like a coffee machine. You input water, coffee beans, and milk (parameters). The machine processes them and returns a coffee (output). You don't care about the internal mechanism — you just use it repeatedly.
๐น Types of Functions in Python
Python offers 6 main types of functions. Understanding each type helps you write cleaner, more efficient code.
1️⃣ Built-in Functions
These come pre-installed with Python. You can use them immediately without defining anything. Examples: print(), len(), type(), int(), sum(), max(), min(), sorted(), range().
# Examples of built-in functions
numbers = [5, 2, 9, 1, 7]
print(len(numbers)) # Output: 5
print(sum(numbers)) # Output: 24
print(max(numbers)) # Output: 9
print(sorted(numbers)) # Output: [1, 2, 5, 7, 9]
print(type(numbers)) # Output: <class 'list'>
2️⃣ User-defined Functions
Functions created by the user using the def keyword. These form the backbone of any Python program.
# User-defined function
def calculate_area(length, width):
"""Returns area of a rectangle"""
return length * width
area = calculate_area(10, 5)
print(area) # Output: 50
3️⃣ Lambda Functions (Anonymous Functions)
Small, one-line functions without a name. Perfect for simple operations where a full function definition is overkill.
# Lambda function
square = lambda x: x ** 2
print(square(5)) # Output: 25
# Used with map, filter, sort
nums = [1, 2, 3, 4, 5]
even_nums = list(filter(lambda x: x % 2 == 0, nums))
print(even_nums) # Output: [2, 4]
4️⃣ Recursive Functions
A function that calls itself to solve a problem by breaking it into smaller sub-problems. Useful for tasks like factorial, Fibonacci, and tree traversal.
# Recursive function to calculate factorial
def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
print(factorial(5)) # Output: 120
5️⃣ Higher-order Functions
Functions that take other functions as arguments or return a function. Examples: map(), filter(), reduce(), sorted().
# Higher-order function: map()
numbers = [1, 2, 3, 4]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # Output: [2, 4, 6, 8]
6️⃣ Nested Functions (Inner Functions)
Functions defined inside another function. They are useful for encapsulation and creating closures.
# Nested function example
def outer_function(message):
def inner_function():
print(f"Message: {message}")
inner_function()
outer_function("Hello from nested function!")
# Output: Message: Hello from nested function!
๐น Defining and Calling Functions
Use the def keyword, followed by the function name, parentheses (), and a colon. The code block must be indented.
# Define a simple function
def greet():
print("Hello, Python learner!")
# Call (invoke) the function
greet() # Output: Hello, Python learner!
๐น Parameters vs Arguments (Important Distinction)
This is a fundamental concept that many beginners confuse. Let's make it crystal clear:
๐ Parameters are the variables listed inside the parentheses in the function definition. They act as placeholders.
๐ Arguments are the actual values passed to the function when it is called.
# Here 'a' and 'b' are PARAMETERS (placeholders)
def multiply(a, b):
return a * b
# Here 5 and 3 are ARGUMENTS (actual values)
result = multiply(5, 3)
print(result) # Output: 15
๐น Return Values
The return statement sends a value back to the caller. Without return, the function returns None.
def square(num):
return num * num
val = square(5)
print(val) # Output: 25
# Returning multiple values (as tuple)
def get_stats(a, b):
return a + b, a - b, a * b
sum_val, diff_val, prod_val = get_stats(10, 3)
print(sum_val, diff_val, prod_val) # Output: 13 7 30
๐นPositional Arguments
Arguments are assigned to parameters based on their order/position.
def order(item, quantity, price):
total = quantity * price
print(f"{quantity} x {item} = ${total}")
# Position matters: item, quantity, price
order("Apple", 5, 0.50) # 5 x Apple = $2.5
order(5, "Apple", 0.50) # Wrong! Apple becomes quantity
๐น Default and Keyword Arguments
Default Arguments
Assign a default value to a parameter. If no argument is provided, the default is used.
def greet_user(name="Guest"):
print(f"Welcome, {name}!")
greet_user() # Output: Welcome, Guest!
greet_user("Maria") # Output: Welcome, Maria!
Keyword Arguments
Specify the parameter name when calling the function. Order doesn't matter.
def profile(name, age, city):
print(f"{name} is {age} years old and lives in {city}")
# Keyword arguments (order doesn't matter)
profile(city="Los Angeles", age=30, name="Bob")
๐น *args and **kwargs (Variable Arguments)
*args (Arbitrary Positional Arguments)
Allows you to pass any number of positional arguments. The arguments are received as a tuple.
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3)) # Output: 6
print(sum_all(10, 20, 30, 40)) # Output: 100
**kwargs (Arbitrary Keyword Arguments)
Allows you to pass any number of keyword arguments. The arguments are received as a dictionary.
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="John", age=28, city="NYC")
๐น Scope: Global vs Local
Local scope – variables defined inside a function.
Global scope – variables defined outside any function.
global_var = "I am global" # Global variable
def show():
local_var = "I am local" # Local variable
print(global_var) # Accessible
show()
๐น Best Practices for Writing Functions
- ✅ Use descriptive names –
calculate_average()notcalc() - ✅ Keep functions small – one function, one task
- ✅ Add docstrings – explain what the function does
- ✅ Avoid modifying global variables
- ✅ Return values instead of printing inside functions
- ✅ Use type hints for better code clarity
๐น Example with Docstring and Type Hints
Docstrings are string literals that appear right after a function definition. They describe what the function does. Type hints (Python 3.5+) specify the expected data types of parameters and return values.
Both make your code more readable, maintainable, and help IDEs provide better autocompletion.
def is_even(number: int) -> bool:
"""
Returns True if the number is even, otherwise False.
Args:
number: An integer to check
Returns:
bool: True if even, False if odd
Example:
>>> is_even(10)
True
>>> is_even(7)
False
"""
return number % 2 == 0
print(is_even(10)) # Output: True
print(is_even(7)) # Output: False
# Another example with multiple type hints
def calculate_bmi(weight: float, height: float) -> float:
"""
Calculate BMI (Body Mass Index).
Args:
weight: Weight in kilograms
height: Height in meters
Returns:
float: BMI value rounded to 2 decimal places
"""
bmi = weight / (height ** 2)
return round(bmi, 2)
bmi = calculate_bmi(70, 1.75)
print(f"BMI: {bmi}") # Output: BMI: 22.86
๐น Real-World Example: Student Grade System
def get_grade(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
def calculate_average(*scores):
if not scores:
return 0
return sum(scores) / len(scores)
# Usage
avg = calculate_average(85, 92, 78)
grade = get_grade(avg)
print(f"Average: {avg}, Grade: {grade}")
๐น Common Mistakes and How to Avoid Them
Even experienced Python developers make these mistakes. Here's how to identify and fix them:
❌ Mistake 1: Forgetting Parentheses
Using my_func instead of my_func() – this refers to the function object itself, not calling it.
# Wrong
def greet():
print("Hello")
greet # Nothing happens - missing parentheses!
# Correct
greet() # Output: Hello
❌ Mistake 2: Modifying Mutable Default Arguments
Using mutable objects (like lists or dictionaries) as default arguments can cause unexpected behavior because defaults are created once when the function is defined, not each time it's called.
# Wrong - Unexpected behavior
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
print(add_item(1)) # Output: [1]
print(add_item(2)) # Output: [1, 2] (Not [2] as expected!)
# Correct - Use None instead
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
print(add_item(1)) # Output: [1]
print(add_item(2)) # Output: [2]
❌ Mistake 3: Inconsistent Return Statements
Some code paths return a value, others return None implicitly – this leads to confusing bugs.
# Wrong - Inconsistent
def check_number(x):
if x > 0:
return "Positive"
# Missing return for x <= 0
print(check_number(5)) # Output: Positive
print(check_number(-3)) # Output: None (Unexpected!)
# Correct - Always return
def check_number(x):
if x > 0:
return "Positive"
else:
return "Negative or Zero"
❌ Mistake 4: Confusing Parameters with Arguments
Remember: Parameters are in the function definition (placeholders). Arguments are the actual values passed when calling.
def multiply(a, b): # 'a' and 'b' are PARAMETERS
return a * b
result = multiply(5, 3) # 5 and 3 are ARGUMENTS
❌ Mistake 5: Using print() Instead of return
print() displays output to the console but cannot be used by other code. return sends a value back to the caller for further use.
# Wrong - Using print()
def add_print(a, b):
print(a + b) # Shows on screen but returns None
result = add_print(5, 3) # Output: 8
print(result) # Output: None
# Correct - Using return
def add_return(a, b):
return a + b
result = add_return(5, 3)
print(result) # Output: 8
๐น Quick Reference Table
| Concept | Syntax Example |
|---|---|
| Basic function | def hello(): print("Hi") |
| Parameters vs Arguments | def add(a,b): → add(5,3) |
| Return value | def add(a,b): return a+b |
| Default argument | def greet(name="User") |
| *args | def sum(*nums): return sum(nums) |
| **kwargs | def info(**data): print(data) |
| Lambda | lambda x: x*2 |
| Recursive | def fact(n): return n*fact(n-1) |
| Docstring | """Function description""" |
| Type Hints | def func(x: int) -> bool |
๐น Frequently Asked Questions (FAQ)
Q1: What's the difference between parameters and arguments?
✅ Parameters are placeholders in function definition; arguments are actual values passed during function call.
Q2: What does *args and **kwargs mean?
✅ *args accepts any number of positional arguments (as tuple). **kwargs accepts any number of keyword arguments (as dict).
Q3: Can a function return multiple values?
✅ Yes, as a tuple: return a, b, c
Q4: What is the difference between return and print?
✅ return sends a value to the caller; print displays output to console.
Q5: What are the 6 types of functions in Python?
✅ Built-in, User-defined, Lambda, Recursive, Higher-order, and Nested functions.
Q6: Why use docstrings and type hints?
✅ Docstrings document your code; type hints improve readability and enable better IDE support.