User-Defined Functions in Python
User-defined functions are created with the def keyword. They let you encapsulate code into reusable blocks.
Basic function definition
Use def followed by the function name and parentheses to define a function.
Functions can take parameters to accept input values.
Function parameters
Functions can accept multiple parameters, including positional and keyword arguments.
# Example 3
def calculate_area(length, width):
return length * width
print(calculate_area(5, 3))
>>> 15
You can use keyword arguments to make function calls more readable.
# Example 4
def create_profile(name, age, city):
return f"{name}, {age}, from {city}"
print(create_profile(age=25, city="London", name="Alice"))
>>> Alice, 25, from London
Functions can accept variable numbers of arguments using *args and **kwargs.
# Example 5
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3, 4, 5))
>>> 15
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="London")
>>> name: Alice
>>> age: 25
>>> city: London
Default arguments
Default arguments provide fallback values when parameters aren't supplied.
# Example 6
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice"))
>>> Hello, Alice!
print(greet("Bob", "Hi"))
>>> Hi, Bob!
Default arguments are evaluated once when the function is defined, not each time it's called.
# Example 7
def add_item(item, items=[]):
items.append(item)
return items
# This can lead to unexpected behaviour
print(add_item("apple"))
>>> ['apple']
print(add_item("banana"))
>>> ['apple', 'banana']
# Better approach: use None as default
def add_item_safe(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item_safe("apple"))
>>> ['apple']
print(add_item_safe("banana"))
>>> ['banana']
Return values
Functions can return values using the return statement. If no return is specified, the function returns None.
Functions can return multiple values as a tuple, which Python automatically unpacks.
# Example 9
def divide_with_remainder(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder
q, r = divide_with_remainder(17, 5)
print(f"Quotient: {q}, Remainder: {r}")
>>> Quotient: 3, Remainder: 2
You can return early from a function.
# Example 10
def check_positive(number):
if number <= 0:
return False
return True
print(check_positive(5))
>>> True
print(check_positive(-3))
>>> False
Function annotations
Type hints can be added to function parameters and return values for documentation and type checking.
Annotations don't enforce types but provide useful documentation.
# Example 12
def process_data(data: list[str], threshold: float = 0.5) -> dict[str, int]:
return {"count": len(data), "threshold": threshold}
result = process_data(["a", "b", "c"], 0.7)
print(result)
>>> {'count': 3, 'threshold': 0.7}
Variable scope
Variables defined inside a function are local to that function. Global variables can be accessed but require the global keyword to modify.
# Example 13
x = 10 # Global variable
def show_scope():
x = 20 # Local variable
print(f"Local x: {x}")
show_scope()
>>> Local x: 20
print(f"Global x: {x}")
>>> Global x: 10
To modify a global variable, use the global keyword.
# Example 14
count = 0
def increment():
global count
count += 1
increment()
increment()
print(count)
>>> 2
Functions can access variables from enclosing scopes, creating closures.
# Example 15
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
add_five = outer_function(5)
print(add_five(3))
>>> 8