Python Functions

Python functions are named blocks of code that execute when called. A Python function can accept input parameters, process data, and return output values. Functions in Python follow the DRY (Don’t Repeat Yourself) principle, allowing you to write code once and use it multiple times throughout your program.

Basic Python Function Syntax

The basic syntax for creating Python functions uses the def keyword:

def function_name(parameters):
"""Optional docstring"""
# Function body
return value # Optional

Let’s create a simple Python function:

def greet_user(name):
"""This function greets a user by name"""
return f"Hello, {name}! Welcome to Python functions."

# Calling the function
message = greet_user("Alice")
print(message) # Output: Hello, Alice! Welcome to Python functions.

Function Parameters in Python

Python functions can accept different types of parameters, making them flexible and powerful.

Positional Parameters

Positional parameters are the most common type of parameters in Python functions:

def calculate_area(length, width):
"""Calculate rectangle area using positional parameters"""
return length * width

area = calculate_area(5, 3)
print(area) # Output: 15

Keyword Arguments

Python functions support keyword arguments, allowing you to specify parameter names when calling the function:

def create_profile(name, age, city):
"""Create user profile with keyword arguments"""
return f"Name: {name}, Age: {age}, City: {city}"

profile = create_profile(city="New York", name="Bob", age=25)
print(profile) # Output: Name: Bob, Age: 25, City: New York

Default Parameters

Python functions can have default parameter values, making some arguments optional:

def send_email(recipient, subject, body="Default message"):
"""Send email with optional body parameter"""
return f"To: {recipient}\nSubject: {subject}\nBody: {body}"

email1 = send_email("john@example.com", "Meeting")
email2 = send_email("jane@example.com", "Update", "Custom message")

Variable-Length Arguments in Python Functions

Python functions can handle variable numbers of arguments using special syntax.

*args (Arbitrary Arguments)

The *args parameter allows Python functions to accept any number of positional arguments:

def sum_numbers(*args):
"""Sum any number of arguments using *args"""
total = 0
for number in args:
total += number
return total

result1 = sum_numbers(1, 2, 3) # Output: 6
result2 = sum_numbers(10, 20, 30, 40) # Output: 100

**kwargs (Keyword Arguments)

The **kwargs parameter allows Python functions to accept any number of keyword arguments:

def build_car(**kwargs):
"""Build car specification using **kwargs"""
car_info = "Car Specifications:\n"
for key, value in kwargs.items():
car_info += f"{key}: {value}\n"
return car_info

car = build_car(brand="Toyota", model="Camry", year=2023, color="Blue")
print(car)

Return Statement in Python Functions

Python functions can return values using the return statement. Functions can return single values, multiple values, or no value at all.

Single Return Value

def calculate_square(number):
"""Return square of a number"""
return number ** 2

square = calculate_square(4)
print(square) # Output: 16

Multiple Return Values

Python functions can return multiple values as tuples:

def get_name_parts(full_name):
"""Split full name into first and last name"""
parts = full_name.split()
first_name = parts[0]
last_name = parts[-1]
return first_name, last_name

first, last = get_name_parts("John Smith")
print(f"First: {first}, Last: {last}") # Output: First: John, Last: Smith

Local vs Global Variables in Python Functions

Understanding variable scope is crucial when working with Python functions.

Local Variables

Variables defined inside Python functions are local and only accessible within that function:

def calculate_discount():
"""Demonstrate local variable scope"""
discount_rate = 0.1 # Local variable
return discount_rate

def apply_discount(price):
"""Apply discount to price"""
local_discount = 0.15 # Local variable
return price * (1 - local_discount)

Global Variables

Global variables can be accessed from anywhere in the program:

tax_rate = 0.08 # Global variable

def calculate_total(price):
"""Calculate total price including tax"""
return price * (1 + tax_rate)

total = calculate_total(100)
print(total) # Output: 108.0

Lambda Functions in Python

Lambda functions are anonymous Python functions that can be defined in a single line:

# Regular function
def multiply_by_two(x):
return x * 2

# Lambda function
multiply_lambda = lambda x: x * 2

# Using lambda with map()
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # Output: [2, 4, 6, 8, 10]

Higher-Order Functions in Python

Python functions can accept other functions as parameters or return functions as results.

Function as Parameter

def apply_operation(numbers, operation):
"""Apply operation function to list of numbers"""
return [operation(num) for num in numbers]

def square(x):
return x ** 2

def cube(x):
return x ** 3

numbers = [1, 2, 3, 4]
squares = apply_operation(numbers, square)
cubes = apply_operation(numbers, cube)

Function Returning Function

def create_multiplier(factor):
"""Return a function that multiplies by factor"""
def multiplier(number):
return number * factor
return multiplier

double = create_multiplier(2)
triple = create_multiplier(3)

print(double(5)) # Output: 10
print(triple(5)) # Output: 15

Recursive Functions in Python

Recursive Python functions call themselves to solve problems by breaking them into smaller subproblems:

def factorial(n):
"""Calculate factorial using recursion"""
if n <= 1:
return 1
else:
return n * factorial(n - 1)

def fibonacci(n):
"""Calculate Fibonacci number using recursion"""
if n <= 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)

print(factorial(5)) # Output: 120
print(fibonacci(6)) # Output: 8

Decorators in Python Functions

Decorators modify or extend the behavior of Python functions without changing their code:

def timer_decorator(func):
"""Decorator to measure function execution time"""
import time
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds")
return result
return wrapper

@timer_decorator
def slow_function():
"""Function with timer decorator"""
import time
time.sleep(1)
return "Function completed"

result = slow_function()

Built-in Functions in Python

Python provides many built-in functions that you can use directly:

# Common built-in functions
numbers = [1, 2, 3, 4, 5]

print(len(numbers)) # Output: 5
print(max(numbers)) # Output: 5
print(min(numbers)) # Output: 1
print(sum(numbers)) # Output: 15
print(sorted(numbers, reverse=True)) # Output: [5, 4, 3, 2, 1]

# Type conversion functions
print(str(123)) # Output: "123"
print(int("456")) # Output: 456
print(float("7.89")) # Output: 7.89

Complete Python Functions Example

Here’s a comprehensive example demonstrating various Python function concepts:

import math
from functools import reduce

# Global configuration
DEFAULT_CURRENCY = "USD"
TAX_RATES = {"USD": 0.08, "EUR": 0.20, "GBP": 0.15}

def calculate_circle_properties(radius):
"""Calculate circle area and circumference"""
area = math.pi * radius ** 2
circumference = 2 * math.pi * radius
return area, circumference

def format_price(amount, currency=DEFAULT_CURRENCY):
"""Format price with currency symbol"""
symbols = {"USD": "$", "EUR": "€", "GBP": "£"}
return f"{symbols.get(currency, '$')}{amount:.2f}"

def apply_discount(price, discount_percent=0):
"""Apply discount to price"""
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Discount must be between 0 and 100")
return price * (1 - discount_percent / 100)

def calculate_tax(price, currency=DEFAULT_CURRENCY):
"""Calculate tax based on currency"""
tax_rate = TAX_RATES.get(currency, 0.08)
return price * tax_rate

def process_order(*items, currency=DEFAULT_CURRENCY, discount=0, **customer_info):
"""Process order with multiple items and customer information"""
print(f"Processing order for {customer_info.get('name', 'Anonymous')}")
print(f"Email: {customer_info.get('email', 'Not provided')}")
print(f"Currency: {currency}")
print("-" * 40)

total = 0
for item in items:
item_name, price = item
discounted_price = apply_discount(price, discount)
tax = calculate_tax(discounted_price, currency)
final_price = discounted_price + tax
total += final_price

print(f"Item: {item_name}")
print(f" Original Price: {format_price(price, currency)}")
print(f" After Discount: {format_price(discounted_price, currency)}")
print(f" Tax: {format_price(tax, currency)}")
print(f" Final Price: {format_price(final_price, currency)}")
print()

print(f"Total Order Amount: {format_price(total, currency)}")
return total

# Lambda function for quick calculations
calculate_compound_interest = lambda principal, rate, time: principal * (1 + rate) ** time

# Higher-order function
def apply_to_prices(prices, operation):
"""Apply operation to list of prices"""
return list(map(operation, prices))

# Recursive function
def calculate_fibonacci_series(n, memo={}):
"""Calculate Fibonacci series with memoization"""
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = calculate_fibonacci_series(n-1, memo) + calculate_fibonacci_series(n-2, memo)
return memo[n]

# Decorator function
def validate_positive(func):
"""Decorator to validate positive numbers"""
def wrapper(value):
if value <= 0:
raise ValueError("Value must be positive")
return func(value)
return wrapper

@validate_positive
def calculate_square_root(number):
"""Calculate square root of positive number"""
return math.sqrt(number)

# Main execution
if __name__ == "__main__":
# Test basic functions
print("=== Circle Properties ===")
area, circumference = calculate_circle_properties(5)
print(f"Circle with radius 5:")
print(f"Area: {area:.2f}")
print(f"Circumference: {circumference:.2f}")
print()

# Test order processing
print("=== Order Processing ===")
order_items = [
("Laptop", 999.99),
("Mouse", 29.99),
("Keyboard", 79.99)
]

total = process_order(
*order_items,
currency="USD",
discount=10,
name="John Doe",
email="john@example.com",
phone="123-456-7890"
)

print()
print("=== Lambda Function Example ===")
investment = calculate_compound_interest(1000, 0.05, 5)
print(f"$1000 invested at 5% for 5 years: ${investment:.2f}")
print()

print("=== Higher-Order Function Example ===")
prices = [10.99, 25.50, 99.99]
discounted_prices = apply_to_prices(prices, lambda x: x * 0.9)
print(f"Original prices: {prices}")
print(f"10% discount applied: {discounted_prices}")
print()

print("=== Recursive Function Example ===")
fib_series = [calculate_fibonacci_series(i) for i in range(10)]
print(f"First 10 Fibonacci numbers: {fib_series}")
print()

print("=== Decorator Example ===")
try:
sqrt_result = calculate_square_root(16)
print(f"Square root of 16: {sqrt_result}")

# This will raise an error due to decorator validation
# sqrt_negative = calculate_square_root(-1)
except ValueError as e:
print(f"Error: {e}")

print()
print("=== Built-in Functions Example ===")
numbers = [45, 23, 78, 12, 56, 89, 34]
print(f"Numbers: {numbers}")
print(f"Length: {len(numbers)}")
print(f"Maximum: {max(numbers)}")
print(f"Minimum: {min(numbers)}")
print(f"Sum: {sum(numbers)}")
print(f"Average: {sum(numbers) / len(numbers):.2f}")
print(f"Sorted: {sorted(numbers)}")

# Using reduce from functools
product = reduce(lambda x, y: x * y, [1, 2, 3, 4, 5])
print(f"Product of [1,2,3,4,5]: {product}")

Expected Output:

=== Circle Properties ===
Circle with radius 5:
Area: 78.54
Circumference: 31.42

=== Order Processing ===
Processing order for John Doe
Email: john@example.com
Currency: USD
----------------------------------------
Item: Laptop
Original Price: $999.99
After Discount: $899.99
Tax: $72.00
Final Price: $971.99

Item: Mouse
Original Price: $29.99
After Discount: $26.99
Tax: $2.16
Final Price: $29.15

Item: Keyboard
Original Price: $79.99
After Discount: $71.99
Tax: $5.76
Final Price: $77.75

Total Order Amount: $1078.89

=== Lambda Function Example ===
$1000 invested at 5% for 5 years: $1276.28

=== Higher-Order Function Example ===
Original prices: [10.99, 25.5, 99.99]
10% discount applied: [9.891, 22.95, 89.991]

=== Recursive Function Example ===
First 10 Fibonacci numbers: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

=== Decorator Example ===
Square root of 16: 4.0

=== Built-in Functions Example ===
Numbers: [45, 23, 78, 12, 56, 89, 34]
Length: 7
Maximum: 89
Minimum: 12
Sum: 337
Average: 48.14
Sorted: [12, 23, 34, 45, 56, 78, 89]
Product of [1,2,3,4,5]: 120

Python functions are fundamental building blocks that make your code more organized, reusable, and maintainable. Whether you’re using basic functions, advanced features like decorators, or built-in functions, mastering Python functions will significantly improve your programming skills. Practice creating different types of Python functions to become proficient in this essential programming concept.