Python Functions Tutorial - def, args, kwargs, return Examples
What is a Function?
A function is a reusable block of code that performs a specific task. Functions help you avoid repetition, organize code, and make it easier to test and maintain.
Defining and Calling Functions
# Define a function
def greet():
print("Hello, World!")
# Call the function
greet() # Hello, World!
# Function with parameters
def greet_user(name):
print(f"Hello, {name}!")
greet_user("Alice") # Hello, Alice!
greet_user("Bob") # Hello, Bob!
# Function with return value
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 8
Parameters and Arguments
# Default parameters
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", greeting="Hey") # Hey, Charlie!
# Keyword arguments - order doesn't matter
def describe(name, age, city):
print(f"{name}, {age}, from {city}")
describe(age=25, city="London", name="Alice")
# *args - variable number of positional arguments
def total(*numbers):
return sum(numbers)
print(total(1, 2, 3)) # 6
print(total(10, 20, 30, 40)) # 100
# **kwargs - variable number of keyword arguments
def show_info(**details):
for key, value in details.items():
print(f"{key}: {value}")
show_info(name="Alice", age=25, city="London")
# Combining all parameter types
def full_example(pos, /, normal, *, kw_only, **kwargs):
print(pos, normal, kw_only, kwargs)
Return Values
# Return multiple values (as a tuple)
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([3, 1, 4, 1, 5, 9])
print(low, high) # 1 9
# Early return
def is_even(n):
if n % 2 == 0:
return True
return False
# Functions without return return None
def say_hi():
print("Hi!")
result = say_hi()
print(result) # None
# Return a function (higher-order function)
def multiplier(factor):
def multiply(x):
return x * factor
return multiply
double = multiplier(2)
triple = multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Type Hints
Type hints (Python 3.5+) make your code more readable and help IDEs catch bugs. They're optional but highly recommended.
def add(a: int, b: int) -> int:
return a + b
def greet(name: str, times: int = 1) -> str:
return (f"Hello, {name}! " * times).strip()
def process(items: list[int]) -> dict[str, int]:
return {"sum": sum(items), "count": len(items)}
# Optional type (can be None)
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id) # returns str or None
print(find_user(1)) # Alice
print(find_user(99)) # None
Docstrings
def calculate_area(radius: float) -> float:
"""
Calculate the area of a circle.
Args:
radius (float): The radius of the circle.
Returns:
float: The area of the circle.
Raises:
ValueError: If radius is negative.
Example:
>>> calculate_area(5)
78.53981633974483
"""
import math
if radius < 0:
raise ValueError("Radius cannot be negative")
return math.pi * radius ** 2
# Access the docstring
print(calculate_area.__doc__)
help(calculate_area)
Recursion
A function that calls itself. Every recursive function needs a base case to stop.
# Factorial: n! = n * (n-1) * ... * 1
def factorial(n: int) -> int:
if n <= 1: # base case
return 1
return n * factorial(n - 1) # recursive call
print(factorial(5)) # 120
print(factorial(10)) # 3628800
# Fibonacci sequence
def fibonacci(n: int) -> int:
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print([fibonacci(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Level Up Your Python Skills
Master Python with these hand-picked resources
10,000+ learners
Free forever
Updated 2026
Key Takeaways
- Functions are defined with def keyword. They are first-class objects - assignable to variables.
- *args collects extra positional arguments as a tuple; **kwargs collects extra keyword arguments as a dict.
- Default parameter values are evaluated once at definition time - avoid mutable defaults like [].
- Lambda functions are anonymous single-expression functions: lambda x: x * 2
- Decorators wrap functions to add behavior without modifying the original function.
- Generators use yield to produce values lazily - memory efficient for large sequences.
- Type hints (def greet(name: str) -> str) improve readability and enable static analysis.
Common Mistakes to Avoid
WRONG
def append_to(element, to=[])
RIGHT
def append_to(element, to=None): if to is None: to = []
Mutable default arguments are shared across all calls. Use None as default and create inside the function.
WRONG
lambda x, y: (x, y) if x > y else (y, x)
RIGHT
def swap_if_needed(x, y): return (x,y) if x>y else (y,x)
Complex lambdas hurt readability. Use a named function for anything beyond a simple expression.
Frequently Asked Questions
Related Python Topics