Complete Guide to Python Functions (2026)

Python Functions Guide

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.

๐Ÿ”น 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 namescalculate_average() not calc()
  • ✅ 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 functiondef hello(): print("Hi")
Parameters vs Argumentsdef add(a,b): → add(5,3)
Return valuedef add(a,b): return a+b
Default argumentdef greet(name="User")
*argsdef sum(*nums): return sum(nums)
**kwargsdef info(**data): print(data)
Lambdalambda x: x*2
Recursivedef fact(n): return n*fact(n-1)
Docstring"""Function description"""
Type Hintsdef 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.