Python Functions
Master the art of writing reusable, efficient code with functions
š§ What are Functions?
Functions are reusable blocks of code that perform specific tasks. They help organize your code, make it more readable, and follow the DRY (Don't Repeat Yourself) principle. Think of functions as mini-programs within your program.
# Simple function example
def greet():
print("Hello!")
# Function with parameter
def greet_name(name):
print(f"Hello {name}!")
# Function with return value
def add(a, b):
return a + b
Creating Your First Function
Functions are defined using the
def
keyword, followed by the function name and parentheses:
# Simple function without parameters
def greet():
"""A simple greeting function"""
print("Hello, World!")
print("Welcome to Python functions!")
# Call the function
greet()
# Function with a single parameter
def greet_person(name):
"""Greet a specific person"""
print(f"Hello, {name}!")
print(f"Nice to meet you, {name}!")
# Call with different names
greet_person("Alice")
greet_person("Bob")
greet_person("Charlie")
# Function with multiple parameters
def introduce_person(name, age, city):
"""Introduce a person with their details"""
print(f"Hi! My name is {name}.")
print(f"I am {age} years old.")
print(f"I live in {city}.")
# Call with positional arguments
introduce_person("Diana", 28, "New York")
introduce_person("Eve", 32, "London")
š” Function Anatomy:
- def : Keyword to define a function
- function_name : Choose descriptive names
- (parameters) : Input values (optional)
- : : Colon to start the function body
- Indented code : The function's logic
Functions That Return Values
Functions can return values using the
return
statement, making them more versatile:
# Basic math functions
def add_numbers(a, b):
"""Add two numbers and return the result"""
result = a + b
return result
def multiply_numbers(x, y):
"""Multiply two numbers and return the result"""
return x * y # Direct return
# Using the functions
sum_result = add_numbers(5, 3)
product_result = multiply_numbers(4, 7)
print(f"5 + 3 = {sum_result}")
print(f"4 Ć 7 = {product_result}")
# Function returning multiple values
def get_name_info(full_name):
"""Extract first and last name from full name"""
parts = full_name.split()
if len(parts) >= 2:
first_name = parts[0]
last_name = parts[-1]
return first_name, last_name
else:
return full_name, ""
# Unpack multiple return values
first, last = get_name_info("John Smith")
print(f"First name: {first}")
print(f"Last name: {last}")
# Function with conditional returns
def calculate_grade(score):
"""Calculate letter grade based on numeric score"""
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
else:
return "F"
# Test grade calculation
test_scores = [95, 87, 73, 65, 45]
for score in test_scores:
grade = calculate_grade(score)
print(f"Score {score}: Grade {grade}")
# Function returning different data types
def analyze_text(text):
"""Analyze text and return various statistics"""
word_count = len(text.split())
char_count = len(text)
char_count_no_spaces = len(text.replace(" ", ""))
is_long = len(text) > 50
return {
"word_count": word_count,
"character_count": char_count,
"characters_no_spaces": char_count_no_spaces,
"is_long_text": is_long,
"first_word": text.split()[0] if text.split() else "",
"last_word": text.split()[-1] if text.split() else ""
}
# Analyze some text
sample_text = "Python functions are powerful tools for organizing code"
analysis = analyze_text(sample_text)
print(f"Text analysis for: '{sample_text}'")
for key, value in analysis.items():
print(f" {key}: {value}")
Default Parameters and Keyword Arguments
Make your functions more flexible with default values and keyword arguments:
# Functions with default parameters
def greet_with_title(name, title="Mr./Ms."):
"""Greet someone with an optional title"""
return f"Hello, {title} {name}!"
# Call with and without title
print(greet_with_title("Smith")) # Uses default title
print(greet_with_title("Johnson", "Dr.")) # Uses custom title
# Multiple default parameters
def create_profile(name, age=25, city="Unknown", occupation="Student"):
"""Create a user profile with default values"""
profile = {
"name": name,
"age": age,
"city": city,
"occupation": occupation
}
return profile
# Different ways to call the function
profile1 = create_profile("Alice")
profile2 = create_profile("Bob", 30)
profile3 = create_profile("Charlie", city="Paris")
profile4 = create_profile("Diana", age=28, occupation="Engineer", city="Tokyo")
print("Profile 1:", profile1)
print("Profile 2:", profile2)
print("Profile 3:", profile3)
print("Profile 4:", profile4)
# Function with mixed parameter types
def format_message(message, prefix="INFO", timestamp=True, uppercase=False):
"""Format a message with various options"""
if timestamp:
from datetime import datetime
time_str = datetime.now().strftime("%H:%M:%S")
formatted = f"[{time_str}] {prefix}: {message}"
else:
formatted = f"{prefix}: {message}"
if uppercase:
formatted = formatted.upper()
return formatted
# Test different combinations
print(format_message("System started"))
print(format_message("Warning detected", prefix="WARN"))
print(format_message("Error occurred", prefix="ERROR", uppercase=True))
print(format_message("Debug info", prefix="DEBUG", timestamp=False))
# Keyword-only arguments (Python 3+)
def calculate_rectangle_area(*, length, width, unit="sq units"):
"""Calculate rectangle area with keyword-only arguments"""
area = length * width
return f"{area} {unit}"
# Must use keyword arguments
# calculate_rectangle_area(5, 3) # This would cause an error
area1 = calculate_rectangle_area(length=5, width=3)
area2 = calculate_rectangle_area(length=10, width=7, unit="sq meters")
print(f"Rectangle area 1: {area1}")
print(f"Rectangle area 2: {area2}")
Variable Arguments (*args and **kwargs)
Handle functions with variable numbers of arguments using *args and **kwargs:
# *args - Variable positional arguments
def calculate_sum(*numbers):
"""Calculate sum of any number of arguments"""
total = 0
for num in numbers:
total += num
return total
# Call with different numbers of arguments
print(f"Sum of 1, 2, 3: {calculate_sum(1, 2, 3)}")
print(f"Sum of 10, 20: {calculate_sum(10, 20)}")
print(f"Sum of 1, 2, 3, 4, 5: {calculate_sum(1, 2, 3, 4, 5)}")
def find_maximum(*values):
"""Find maximum value from any number of arguments"""
if not values:
return None
max_val = values[0]
for val in values[1:]:
if val > max_val:
max_val = val
return max_val
print(f"Maximum of 3, 7, 2, 9, 1: {find_maximum(3, 7, 2, 9, 1)}")
# **kwargs - Variable keyword arguments
def create_student_record(**student_info):
"""Create a student record from keyword arguments"""
record = {"type": "student"}
record.update(student_info)
return record
# Call with different keyword arguments
student1 = create_student_record(name="Alice", age=20, major="Computer Science")
student2 = create_student_record(name="Bob", age=22, major="Mathematics", gpa=3.8)
student3 = create_student_record(name="Charlie", age=19, major="Physics",
year="Sophomore", scholarship=True)
print("Student 1:", student1)
print("Student 2:", student2)
print("Student 3:", student3)
# Combining regular parameters, *args, and **kwargs
def process_order(customer_name, *items, **options):
"""Process an order with customer name, items, and options"""
print(f"Processing order for: {customer_name}")
print(f"Items ordered: {', '.join(items)}")
if options:
print("Special options:")
for key, value in options.items():
print(f" {key}: {value}")
total_items = len(items)
return f"Order processed: {total_items} items for {customer_name}"
# Test the flexible function
result1 = process_order("John", "Pizza", "Soda")
print(result1)
print()
result2 = process_order("Jane", "Burger", "Fries", "Shake",
delivery=True, tip=5.00, special_instructions="Extra sauce")
print(result2)
print()
# Advanced: Function that accepts and forwards arguments
def logged_function_call(func, *args, **kwargs):
"""Call a function and log the call details"""
print(f"Calling function: {func.__name__}")
print(f"Arguments: {args}")
print(f"Keyword arguments: {kwargs}")
result = func(*args, **kwargs)
print(f"Function returned: {result}")
return result
# Test with our previous functions
print("=== Logged Function Calls ===")
logged_function_call(calculate_sum, 1, 2, 3, 4)
print()
logged_function_call(create_student_record, name="Test Student", age=21, major="Testing")
Advanced Function Concepts
Explore advanced concepts like nested functions, closures, and decorators:
# Nested functions and closures
def create_multiplier(factor):
"""Create a function that multiplies by a specific factor"""
def multiply(number):
return number * factor
return multiply
# Create specialized multiplier functions
double = create_multiplier(2)
triple = create_multiplier(3)
times_ten = create_multiplier(10)
print(f"Double 5: {double(5)}")
print(f"Triple 7: {triple(7)}")
print(f"Times ten 3: {times_ten(3)}")
# Function as first-class objects
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
return a / b if b != 0 else "Cannot divide by zero"
# Store functions in a dictionary
operations = {
"+": add,
"-": subtract,
"*": multiply,
"/": divide
}
def calculate(a, operator, b):
"""Perform calculation using function lookup"""
if operator in operations:
return operations[operator](a, b)
else:
return "Unknown operator"
# Test the calculator
print(f"10 + 5 = {calculate(10, '+', 5)}")
print(f"10 - 3 = {calculate(10, '-', 3)}")
print(f"4 * 7 = {calculate(4, '*', 7)}")
print(f"15 / 3 = {calculate(15, '/', 3)}")
# Simple decorator example
def timing_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()
execution_time = end_time - start_time
print(f"Function {func.__name__} took {execution_time:.4f} seconds")
return result
return wrapper
# Apply decorator
@timing_decorator
def slow_function():
"""A function that takes some time to execute"""
import time
time.sleep(0.1) # Simulate slow operation
return "Task completed"
# Test decorated function
result = slow_function()
print(f"Result: {result}")
# Recursive functions
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)
# Test recursive functions
print(f"Factorial of 5: {factorial(5)}")
print(f"Fibonacci sequence (first 10 numbers):")
for i in range(10):
print(f"F({i}) = {fibonacci(i)}")
# Lambda functions (anonymous functions)
# Simple lambda examples
square = lambda x: x ** 2
add_ten = lambda x: x + 10
is_even = lambda x: x % 2 == 0
print(f"Square of 6: {square(6)}")
print(f"Add 10 to 15: {add_ten(15)}")
print(f"Is 8 even? {is_even(8)}")
# Using lambda with built-in functions
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Filter even numbers
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Even numbers: {even_numbers}")
# Square all numbers
squared_numbers = list(map(lambda x: x ** 2, numbers))
print(f"Squared numbers: {squared_numbers}")
# Sort by custom criteria
students = [
{"name": "Alice", "grade": 85},
{"name": "Bob", "grade": 92},
{"name": "Charlie", "grade": 78},
{"name": "Diana", "grade": 96}
]
# Sort by grade (descending)
sorted_students = sorted(students, key=lambda student: student["grade"], reverse=True)
print("Students sorted by grade (highest first):")
for student in sorted_students:
print(f" {student['name']}: {student['grade']}")
Real-World Example: Task Management System
Here's a comprehensive example showing how functions work together in a real application:
"""
Task Management System
Demonstrates comprehensive use of Python functions
"""
from datetime import datetime, timedelta
import json
# Global task storage
tasks = []
task_id_counter = 1
def generate_task_id():
"""Generate unique task ID"""
global task_id_counter
current_id = task_id_counter
task_id_counter += 1
return current_id
def create_task(title, description="", priority="medium", due_date=None):
"""Create a new task with specified parameters"""
task = {
"id": generate_task_id(),
"title": title,
"description": description,
"priority": priority,
"status": "pending",
"created_at": datetime.now().isoformat(),
"due_date": due_date,
"completed_at": None
}
tasks.append(task)
return task
def get_task_by_id(task_id):
"""Find and return a task by its ID"""
for task in tasks:
if task["id"] == task_id:
return task
return None
def update_task(task_id, **updates):
"""Update task fields using keyword arguments"""
task = get_task_by_id(task_id)
if task:
for key, value in updates.items():
if key in task:
task[key] = value
return task
return None
def complete_task(task_id):
"""Mark a task as completed"""
task = get_task_by_id(task_id)
if task:
task["status"] = "completed"
task["completed_at"] = datetime.now().isoformat()
return True
return False
def delete_task(task_id):
"""Delete a task by ID"""
global tasks
tasks = [task for task in tasks if task["id"] != task_id]
def filter_tasks(status=None, priority=None, overdue_only=False):
"""Filter tasks based on various criteria"""
filtered = tasks.copy()
if status:
filtered = [task for task in filtered if task["status"] == status]
if priority:
filtered = [task for task in filtered if task["priority"] == priority]
if overdue_only:
current_time = datetime.now()
filtered = [
task for task in filtered
if task["due_date"] and
datetime.fromisoformat(task["due_date"]) < current_time and
task["status"] != "completed"
]
return filtered
def sort_tasks(tasks_list, sort_by="created_at", reverse=False):
"""Sort tasks by specified field"""
if sort_by == "priority":
priority_order = {"high": 3, "medium": 2, "low": 1}
return sorted(tasks_list,
key=lambda task: priority_order.get(task["priority"], 0),
reverse=reverse)
else:
return sorted(tasks_list, key=lambda task: task.get(sort_by, ""), reverse=reverse)
def get_task_statistics():
"""Calculate and return task statistics"""
total_tasks = len(tasks)
completed_tasks = len([task for task in tasks if task["status"] == "completed"])
pending_tasks = total_tasks - completed_tasks
priority_counts = {"high": 0, "medium": 0, "low": 0}
for task in tasks:
priority = task.get("priority", "medium")
priority_counts[priority] += 1
# Calculate overdue tasks
current_time = datetime.now()
overdue_tasks = len([
task for task in tasks
if task["due_date"] and
datetime.fromisoformat(task["due_date"]) < current_time and
task["status"] != "completed"
])
return {
"total": total_tasks,
"completed": completed_tasks,
"pending": pending_tasks,
"overdue": overdue_tasks,
"by_priority": priority_counts,
"completion_rate": (completed_tasks / total_tasks * 100) if total_tasks > 0 else 0
}
def format_task_display(task, show_details=False):
"""Format task for display"""
status_icon = "ā
" if task["status"] == "completed" else "ā³"
priority_icon = {"high": "š“", "medium": "š”", "low": "š¢"}.get(task["priority"], "āŖ")
basic_info = f"{status_icon} [{task['id']}] {task['title']} {priority_icon}"
if show_details:
details = []
if task["description"]:
details.append(f"Description: {task['description']}")
if task["due_date"]:
due_date = datetime.fromisoformat(task["due_date"])
details.append(f"Due: {due_date.strftime('%Y-%m-%d %H:%M')}")
details.append(f"Priority: {task['priority'].title()}")
details.append(f"Status: {task['status'].title()}")
if details:
return basic_info + "\n " + "\n ".join(details)
return basic_info
def export_tasks_to_json(filename="tasks.json"):
"""Export tasks to JSON file"""
try:
with open(filename, 'w') as file:
json.dump(tasks, file, indent=2)
return f"Tasks exported to {filename}"
except Exception as e:
return f"Error exporting tasks: {e}"
def import_tasks_from_json(filename="tasks.json"):
"""Import tasks from JSON file"""
global tasks, task_id_counter
try:
with open(filename, 'r') as file:
imported_tasks = json.load(file)
tasks.extend(imported_tasks)
# Update task ID counter
if tasks:
max_id = max(task["id"] for task in tasks)
task_id_counter = max_id + 1
return f"Imported {len(imported_tasks)} tasks from {filename}"
except FileNotFoundError:
return f"File {filename} not found"
except Exception as e:
return f"Error importing tasks: {e}"
# Demo the task management system
def demo_task_system():
"""Demonstrate the task management system"""
print("šļø TASK MANAGEMENT SYSTEM DEMO")
print("=" * 50)
# Create some sample tasks
task1 = create_task(
"Complete Python tutorial",
"Finish learning about functions",
priority="high",
due_date=(datetime.now() + timedelta(days=2)).isoformat()
)
task2 = create_task(
"Buy groceries",
"Milk, bread, eggs, fruits",
priority="medium",
due_date=(datetime.now() + timedelta(days=1)).isoformat()
)
task3 = create_task(
"Exercise",
"30 minutes cardio workout",
priority="low"
)
task4 = create_task(
"Submit report",
"Monthly progress report",
priority="high",
due_date=(datetime.now() - timedelta(days=1)).isoformat() # Overdue
)
print("š Created sample tasks:")
for task in tasks:
print(f" {format_task_display(task)}")
# Complete a task
print(f"\nā
Completing task {task2['id']}...")
complete_task(task2["id"])
# Update a task
print(f"\nš Updating task {task3['id']}...")
update_task(task3["id"], description="45 minutes workout including weights")
# Show statistics
print(f"\nš TASK STATISTICS:")
stats = get_task_statistics()
print(f" Total tasks: {stats['total']}")
print(f" Completed: {stats['completed']}")
print(f" Pending: {stats['pending']}")
print(f" Overdue: {stats['overdue']}")
print(f" Completion rate: {stats['completion_rate']:.1f}%")
print(f" Priority breakdown: {stats['by_priority']}")
# Show filtered tasks
print(f"\nš HIGH PRIORITY TASKS:")
high_priority = filter_tasks(priority="high")
for task in high_priority:
print(f" {format_task_display(task, show_details=True)}")
print(f"\nā ļø OVERDUE TASKS:")
overdue = filter_tasks(overdue_only=True)
for task in overdue:
print(f" {format_task_display(task, show_details=True)}")
# Show sorted tasks
print(f"\nš ALL TASKS (sorted by priority):")
sorted_tasks = sort_tasks(tasks, sort_by="priority", reverse=True)
for task in sorted_tasks:
print(f" {format_task_display(task)}")
# Run the demo
if __name__ == "__main__":
demo_task_system()
š§ Test Your Knowledge
Test your understanding of Python functions: