Python Functions Explained — Complete Step-by-Step Guide with Examples
Functions are reusable blocks of code that take inputs, perform actions, and return outputs. Think of a function like a vending machine — you put in money and a selection (inputs), the machine processes your request, and out comes a snack (output).
What You’ll Learn
- How to define and call functions with
def - How parameters, arguments, and return values work
- The difference between
*argsand**kwargs - What variable scope means and how Python resolves names
- Lambda functions, type hints, and docstrings explained simply
- Common mistakes that trip up beginners and how to fix them
Why Functions Matter
Without functions, every program would be a single massive block of code — impossible to read, test, or reuse. Imagine if DodaZIP had to rewrite its file compression logic every time it needed to zip a file. Instead, it calls a compression function. Durga Antivirus Pro uses functions for signature checking, behavioral analysis, and quarantine — each is a separate, testable unit. Functions let you write once, use anywhere.
flowchart LR
A["Python Basics"] --> B["Control Flow"]
B --> C["Functions"]
C --> D["Lists & Dicts"]
D --> E["Modules & Packages"]
E --> F["File I/O & Errors"]
A:::done --> B:::done --> C:::current --> D
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#2563eb,stroke:#2563eb,color:#fff
style C fill:#2563eb,stroke:#2563eb,color:#fff
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
style F fill:#f1f5f9,stroke:#94a3b8,color:#64748b
Defining Functions
You define a function with the def keyword. Here’s the anatomy:
def greet(name):
return f"Hello, {name}!"
print(greet("Alice")) # "Hello, Alice!"
print(greet("Bob")) # "Hello, Bob!"Let’s break this down:
def— Python keyword that says “I’m defining a function”greet— the function name (use lowercase with underscores, likecalculate_total)(name)— parameters the function accepts (like slots for inputs):— colon marks the start of the function bodyreturn— sends a value back to wherever the function was called- The indented block — the code that runs when the function is called
When Python runs greet("Alice"), it: (1) assigns "Alice" to the parameter name, (2) runs the body, (3) returns the result, (4) the print() function displays it.
| Part | Meaning |
|---|---|
def | Keyword that starts a function definition |
greet | Function name |
name | Parameter — receives the input value |
return | Sends value back to caller. Without it, returns None |
| body | The indented block that runs when called |
Parameters
Parameters are the inputs your function accepts. They’re like the settings on a coffee machine — you can customize what goes in.
Default Values
def greet(name="Guest", greeting="Hello"):
return f"{greeting}, {name}!"
print(greet()) # "Hello, Guest!"
print(greet("Alice")) # "Hello, Alice!"
print(greet("Bob", "Hi")) # "Hi, Bob!"[] or {} — use None instead.Positional vs Keyword Arguments
def describe(name, age, role):
return f"{name} is {age} and works as a {role}"
# Positional (order matters)
print(describe("Alice", 25, "Dev"))
# Keyword (order doesn't matter — more readable)
print(describe(role="Dev", age=25, name="Alice"))Both calls produce the same output. Keyword arguments make your code self-documenting — you can see what each value means.
*args and **kwargs
Sometimes you don’t know how many arguments you’ll receive. *args packs extra positional arguments into a tuple. **kwargs packs extra keyword arguments into a dict.
# *args — any number of positional args
def sum_all(*numbers):
return sum(numbers)
print(sum_all(1, 2, 3, 4)) # 10
# **kwargs — any number of keyword args
def print_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, role="Dev")The parameter order must be: normal params → *args → default params → **kwargs.
Return Values
A function can return one value, multiple values, or nothing.
# Single value
def square(x):
return x * x
# Multiple values (packed as a tuple)
def min_max(lst):
return min(lst), max(lst)
low, high = min_max([3, 1, 7, 2, 9])
print(low, high) # 1 9
# No return → None
def log(msg):
print(msg)
# implicit: return NoneScope — Where Variables Live
Think of scope like rooms in a house. A variable defined inside a function (a room) can’t be seen from outside that room.
global_var = "I'm global"
def demo():
local_var = "I'm local"
print(global_var) # accessible (read-only)
print(local_var) # accessible
demo()
print(global_var) # accessible
# print(local_var) # NameError! — local_var doesn't exist hereTo modify a global variable inside a function, use global:
counter = 0
def increment():
global counter
counter += 1
increment()
print(counter) # 1global when possible. Pass values as parameters and return results instead — it makes your code easier to test and reason about.Lambda Functions
A lambda is a tiny anonymous function — useful for short operations where writing a full def feels like overkill:
# Regular function
def double(x):
return x * 2
# Lambda (one expression only, no return keyword needed)
double = lambda x: x * 2
print(double(5)) # 10Common real use: sorting a list of dicts:
students = [
{"name": "Alice", "grade": 85},
{"name": "Bob", "grade": 92},
{"name": "Charlie", "grade": 78},
]
students.sort(key=lambda s: s["grade"], reverse=True)
print(students)
# Bob (92), Alice (85), Charlie (78)Type Hints (Python 3.5+)
Type hints document what types a function expects and returns. They’re optional and not enforced at runtime, but tools like mypy and IDE autocompletion use them:
def greet(name: str, age: int) -> str:
return f"{name} is {age} years old"Docstrings
A docstring describes what a function does. Triple-quoted strings right after the def line:
def calculate(a: float, b: float, operation: str) -> float:
"""Perform basic arithmetic operations.
Args:
a: First number
b: Second number
operation: 'add', 'subtract', 'multiply', or 'divide'
Returns:
Result of the operation
"""
operations = {
"add": a + b,
"subtract": a - b,
"multiply": a * b,
"divide": a / b if b != 0 else float("inf"),
}
return operations.get(operation, 0)You can view any function’s docstring with help(function_name).
Common Mistakes
1. Forgetting the Return Statement
def square(x):
result = x * x
# missing return!
print(square(5)) # None — not 25!Fix: Always end value-producing functions with an explicit return.
2. Using Mutable Default Arguments
def add_item(item, items=[]): # BAD
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — same list reused!Fix: Use None and create a new list inside:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items3. Wrong *args / **kwargs Order
def func(**kwargs, *args): # SyntaxError!
passFix: normal params → *args → default params → **kwargs.
4. Modifying a Global Without global
count = 0
def increment():
count += 1 # UnboundLocalError!Fix: Use global count inside, or (better) pass it as a parameter and return the updated value.
5. Forgetting Parentheses When Calling
def greet():
return "Hello"
print(greet) # <function greet at 0x...> — the object, not the result!
print(greet()) # "Hello" — correctFix: Always add () to call a function.
6. Confusing Parameters and Arguments
Parameters are the variables in the function definition. Arguments are the values you pass when calling. def greet(name): — name is a parameter. greet("Alice") — "Alice" is an argument.
Practice Questions
1. What does this code return?
def add(a, b):
return a + b
result = add(3, 4)7. The function takes a=3, b=4, returns their sum.
2. Why does this print None?
def greet(name):
print(f"Hello, {name}")
print(greet("Alice"))The function prints “Hello, Alice” inside, but has no return statement, so it returns None. The print(greet(...)) then prints that None.
3. Fix this bug:
def append_to(item, list=[]):
list.append(item)
return listThe default list [] is shared across all calls. Fix with list=None and create a new list inside.
4. What’s the output?
x = 10
def change():
x = 5
change()
print(x)10. The x = 5 inside the function creates a local variable, it doesn’t modify the global x.
Challenge: Write a function is_palindrome(s) that returns True if a string reads the same forward and backward (ignoring case and spaces). Example: "racecar" → True, "A man a plan a canal panama" → True.
Solution
def is_palindrome(s: str) -> bool:
cleaned = s.lower().replace(" ", "")
return cleaned == cleaned[::-1]FAQ
Try It Yourself
Run this to see functions in action:
def factorial(n):
"""Calculate n! recursively."""
if n <= 1:
return 1
return n * factorial(n - 1)
def apply_twice(func, value):
return func(func(value))
print(f"factorial(5) = {factorial(5)}")
print(f"apply_twice(lambda x: x+5, 10) = {apply_twice(lambda x: x+5, 10)}")Expected output:
factorial(5) = 120
apply_twice(lambda x: x+5, 10) = 20Mini Project: Task Manager
Build a simple task manager using functions:
tasks = []
def add_task(name: str, priority: str = "medium") -> None:
"""Add a new task to the list."""
tasks.append({"name": name, "priority": priority, "done": False})
print(f"Added: {name}")
def list_tasks() -> None:
"""Display all tasks."""
if not tasks:
print("No tasks yet!")
return
for i, task in enumerate(tasks, 1):
status = "✓" if task["done"] else " "
print(f"{i}. [{status}] {task['name']} ({task['priority']})")
def complete_task(index: int) -> None:
"""Mark a task as done by its number."""
try:
tasks[index - 1]["done"] = True
print(f"Task {index} completed!")
except IndexError:
print("Invalid task number!")
# Try it out
add_task("Learn Python functions", "high")
add_task("Build a mini project", "high")
add_task("Review common mistakes", "medium")
list_tasks()
print()
complete_task(1)
list_tasks()Expected output:
Added: Learn Python functions
Added: Build a mini project
Added: Review common mistakes
1. [ ] Learn Python functions (high)
2. [ ] Build a mini project (high)
3. [ ] Review common mistakes (medium)
Task 1 completed!
1. [✓] Learn Python functions (high)
2. [ ] Build a mini project (high)
3. [ ] Review common mistakes (medium)What’s Next
Now that you understand functions, learn how to work with collections of data.
| Topic | Description | Link |
|---|---|---|
| Python Lists & Dicts | Work with collections | https://tutorials.dodatech.com/programming-languages/python/py-lists-dicts/ |
| Python Modules & Packages | Organize and reuse code | https://tutorials.dodatech.com/programming-languages/python/py-modules/ |
| Django | Web framework that uses functions extensively | Django |
Practice tip: Extend the task manager with a show_by_priority(level) function that filters tasks. The best way to learn functions is to write your own!
What’s Next
Congratulations on completing this Py Functions tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro