For AI agents: use /llms.txt for the Nakafa content index.
A function names a reusable operation. Its parameters describe the inputs it accepts, its body performs the work, and a return statement can provide a result to the caller.
In programming, functions help us avoid writing the same code repeatedly. Functions have names, can accept parameters (input data), and can return values.
Every function in Python has a basic structure consisting of several important components.
def function_name(parameter_list):
"""Optional docstring to explain the function"""
# Code block to be executed
statement_1
statement_2
return return_value # OptionalFunction components consist of:
def keyword to start function definitionreturn statement to return a valueCalling a function evaluates the callable expression, binds the supplied arguments to parameters, and executes the function body in a new local frame.
# Function definition
def greet(name):
message = f"Hello, {name}!"
return message
# Function call
result = greet("Alice")
print(result) # Output: Hello, Alice!
# Direct call in print
print(greet("Bob")) # Output: Hello, Bob!Process that occurs during function call:
return ends the call with a value; reaching the end returns NoneParameters are variables defined in functions, while arguments are actual values sent when calling functions. Python provides several types of parameters for greater flexibility.
def introduction(name, age, city="Jakarta"):
return f"My name is {name}, {age} years old, living in {city}"
# Using positional parameters
print(introduction("Sari", 25))
# Output: My name is Sari, 25 years old, living in Jakarta
# Using keyword parameters
print(introduction(age=30, name="Budi", city="Bandung"))
# Output: My name is Budi, 30 years old, living in Bandung
# Mix of positional and keyword parameters
print(introduction("Andi", age=28, city="Surabaya"))
# Output: My name is Andi, 28 years old, living in SurabayaImportant rules in parameter usage:
Python allows a signature to collect a variable number of arguments with *args and **kwargs. Practical limits still come from available resources and the work performed by the function.
def calculate_total(*numbers):
"""Calculate total from a number of numbers"""
total = 0
for num in numbers:
total += num
return total
# Calling with various number of arguments
print(calculate_total(1, 2, 3)) # Output: 6
print(calculate_total(5, 10, 15, 20)) # Output: 50
def student_info(name, **details):
"""Display student information with additional details"""
print(f"Name: {name}")
for key, value in details.items():
print(f"{key.capitalize()}: {value}")
# Calling with keyword arguments
student_info("Maya", age=20, major="Informatics", gpa=3.8)
# Output:
# Name: Maya
# Age: 20
# Major: Informatics
# Gpa: 3.8Parameter *args collects additional positional arguments into a tuple, while **kwargs collects additional keyword arguments into a dictionary.
Functions can return values using the return statement. If there's no return or return without a value, the function will return None.
def circle_area(radius):
"""Calculate circle area"""
import math
return math.pi * radius ** 2
def find_min_max(number_list):
"""Return minimum and maximum values"""
if not number_list:
return None, None
return min(number_list), max(number_list)
def print_message(message):
"""Function without explicit return"""
print(f"Message: {message}")
# No return, automatically return None
# Usage example
area = circle_area(5)
print(f"Circle area: {area:.2f}") # Output: Circle area: 78.54
min_val, max_val = find_min_max([3, 1, 4, 1, 5, 9])
print(f"Min: {min_val}, Max: {max_val}") # Output: Min: 1, Max: 9
result = print_message("Hello World") # Output: Message: Hello World
print(f"print_message function result: {result}") # Output: print_message function result: NoneVariables in Python have scope that determines where variables can be accessed. Understanding variable scope is important to avoid errors in programs.
# Global variable
counter = 0
def add_counter():
# Local variable with same name
counter = 10
print(f"Local counter: {counter}")
def add_global_counter():
global counter
counter += 1
print(f"Global counter: {counter}")
# Usage demonstration
print(f"Initial counter: {counter}") # Output: Initial counter: 0
add_counter() # Output: Local counter: 10
print(f"Counter after function: {counter}") # Output: Counter after function: 0
add_global_counter() # Output: Global counter: 1
print(f"Final counter: {counter}") # Output: Final counter: 1Variable lookup rules follow LEGB order:
In Python, functions are first-class objects, meaning functions can be treated like other data. You can store functions in variables, pass functions as arguments, or return functions from other functions.
def multiply_two(x):
return x * 2
def multiply_three(x):
return x * 3
def apply_operation(function, value):
"""Apply function to value"""
return function(value)
# Store function in variable
operation = multiply_two
print(operation(5)) # Output: 10
# Store functions in list
operation_list = [multiply_two, multiply_three]
for op in operation_list:
print(op(4)) # Output: 8 then 12
# Pass function as argument
result1 = apply_operation(multiply_two, 7)
result2 = apply_operation(multiply_three, 7)
print(f"Results: {result1}, {result2}") # Output: Results: 14, 21Docstring is a string literal that appears as the first statement in a function definition. Docstring serves as documentation to explain the purpose and usage of the function.
def calculate_factorial(n):
"""
Calculate factorial of a positive integer.
Parameters:
n (int): Nonnegative integer
Returns:
int: Factorial value of n
Raises:
ValueError: If n is negative
TypeError: If n is not an integer
Example:
>>> calculate_factorial(5)
120
>>> calculate_factorial(0)
1
"""
if not isinstance(n, int):
raise TypeError("Input must be an integer")
if n < 0:
raise ValueError("Input must be positive or zero")
if n <= 1:
return 1
return n * calculate_factorial(n - 1)
# Access docstring
print(calculate_factorial.__doc__)
# Use function
print(calculate_factorial(5)) # Output: 120
print(calculate_factorial(0)) # Output: 1Good docstring writing conventions:
Python allows a function definition inside another function. When the returned inner function retains a free variable from an enclosing scope, the function and that retained environment form a closure.
def create_multiplier(factor):
"""Create multiplier function with specific factor"""
def multiplier(value):
"""Inner function that multiplies value by factor"""
return value * factor
return multiplier
# Create specific multiplier functions
multiply_two = create_multiplier(2)
multiply_five = create_multiplier(5)
print(multiply_two(10)) # Output: 20
print(multiply_five(4)) # Output: 20
def simple_calculator():
"""Calculator with nested functions"""
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
# Return dictionary containing functions
return {
'add': add,
'subtract': subtract,
'multiply': multiply
}
# Use calculator
calc = simple_calculator()
print(calc['add'](5, 3)) # Output: 8
print(calc['subtract'](10, 4)) # Output: 6
print(calc['multiply'](6, 7)) # Output: 42A function should state which inputs it accepts and fail explicitly when that contract is violated. Catch an exception only where code can recover, add useful context, or present the failure to a user.
def divide(numerator, denominator):
"""Divide two real numbers with an explicit input contract."""
if not isinstance(numerator, (int, float)):
raise TypeError("Numerator must be a real number")
if not isinstance(denominator, (int, float)):
raise TypeError("Denominator must be a real number")
if denominator == 0:
raise ValueError("Denominator must not be zero")
return numerator / denominator
def convert_to_int(value):
"""Convert a supported value or preserve Python's conversion error."""
return int(value)
# Successful calls stay direct
print(f"Division result: {divide(10, 2)}")
# Output: Division result: 5.0
print(convert_to_int("123"))
# Output: 123
# Catch at the presentation boundary where recovery is possible
for value in ("abc", [1, 2]):
try:
print(convert_to_int(value))
except (TypeError, ValueError) as error:
print(f"Could not convert {value!r}: {error}")