Python Functions Reference
By Engr Aamir Jamil
Basic Functions
Function Definition and Syntax
python
# Basic function
def greet():
print("Hello World!")
greet() # Call the function
# Function with parameters
def greet_person(name):
print(f"Hello, {name}!")
greet_person("Aamir")
# Function with return value
def add_numbers(a, b):
return a + b
result = add_numbers(5, 3) # result = 8
Function Parameters and Arguments
Default Parameters
python
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Aamir")) # Hello, Aamir!
print(greet("Aamir", "Hi")) # Hi, Aamir!
print(greet("Aamir", greeting="Hey")) # Hey, Aamir!
Variable-Length Arguments
python
# *args - Variable positional arguments
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3)) #6
print(sum_all(1, 2, 3, 4, 5)) # 15
# **kwargs - Variable keyword arguments
def print_info(**kwargs):
for key, value in [Link]():
print(f"{key}: {value}")
print_info(name="Aamir", age=30, city="Rawalpindi")
# Combined parameters
def flexible_function(required, *args, **kwargs):
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
flexible_function("must_have", 1, 2, 3, name="Aamir", age=30)
Keyword-Only and Positional-Only Arguments
python
# Positional-only arguments (Python 3.8+)
def positional_only(a, b, /):
return a + b
# Keyword-only arguments
def keyword_only(*, name, age):
return f"{name} is {age} years old"
# Mixed parameters
def mixed_params(pos_only, /, standard, *, kw_only):
return f"{pos_only}, {standard}, {kw_only}"
# Usage
result1 = positional_only(1, 2) # OK
result2 = keyword_only(name="Aamir", age=30) # OK
result3 = mixed_params("first", "second", kw_only="third")
Functions and Variable Scope
Local vs Global Scope
python
# Global variable
global_var = "I'm global"
def test_scope():
# Local variable
local_var = "I'm local"
print(global_var) # Can access global
print(local_var) # Can access local
test_scope()
# print(local_var) # Error: local_var not accessible outside function
Global Keyword
python
counter = 0 # Global variable
def increment():
global counter # Declare we want to modify global
counter += 1
def get_counter():
return counter # Can read global without 'global' keyword
increment()
print(get_counter()) # 1
increment()
print(get_counter()) # 2
Nonlocal Keyword (Enclosing Scope)
python
def outer_function():
x = 10 # Enclosing scope variable
def inner_function():
nonlocal x # Refer to enclosing scope x
x += 5
print(f"Inner x: {x}")
print(f"Outer x before: {x}")
inner_function()
print(f"Outer x after: {x}")
outer_function()
# Output: Outer x before: 10, Inner x: 15, Outer x after: 15
LEGB Rule (Local, Enclosing, Global, Built-in)
python
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(f"Inner function x: {x}") # local
inner()
print(f"Outer function x: {x}") # enclosing
outer()
print(f"Global x: {x}") # global
# Built-in scope example
def test_builtin():
# len is from built-in scope
return len([1, 2, 3]) # Uses built-in len() function
Lambda Expressions
Basic Lambda Functions
python
# Regular function
def square(x):
return x ** 2
# Equivalent lambda
square_lambda = lambda x: x ** 2
print(square(5)) # 25
print(square_lambda(5)) # 25
# Multiple parameters
add = lambda x, y: x + y
print(add(3, 4)) #7
# Lambda with default parameters
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Aamir")) # Hello, Aamir!
Lambda in Practical Use
python
# Sorting with lambda
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
# Sort by grade (second element)
by_grade = sorted(students, key=lambda student: student[1])
print(by_grade) # [('Charlie', 78), ('Alice', 85), ('Bob', 92)]
# Sort by name length
by_name_length = sorted(students, key=lambda student: len(student[0]))
# Dictionary sorting
person = {"name": "Aamir", "age": 30, "salary": 50000}
sorted_items = sorted([Link](), key=lambda item: item[1])
Lambda Limitations and Best Practices
python
# Good use cases for lambda
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
# Avoid complex lambdas - use regular functions instead
# Bad: complex lambda
# complex_lambda = lambda x: x**2 if x > 0 else abs(x) if x < 0 else 0
# Good: regular function for complex logic
def process_number(x):
if x > 0:
return x ** 2
elif x < 0:
return abs(x)
else:
return 0
Map and Filter Functions
Map Function
python
# map() applies function to each element
numbers = [1, 2, 3, 4, 5]
# Using lambda with map
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# Using regular function with map
def fahrenheit_to_celsius(f):
return (f - 32) * 5/9
fahrenheit_temps = [32, 68, 86, 104]
celsius_temps = list(map(fahrenheit_to_celsius, fahrenheit_temps))
print(celsius_temps) # [0.0, 20.0, 30.0, 40.0]
# Map with multiple iterables
def multiply(x, y):
return x * y
list1 = [1, 2, 3, 4]
list2 = [2, 3, 4, 5]
products = list(map(multiply, list1, list2))
print(products) # [2, 6, 12, 20]
Filter Function
python
# filter() filters elements based on condition
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(even_numbers) # [2, 4, 6, 8, 10]
# Filter with regular function
def is_positive(x):
return x > 0
mixed_numbers = [-3, -1, 0, 2, 5, -2]
positive_numbers = list(filter(is_positive, mixed_numbers))
print(positive_numbers) # [2, 5]
# Filter strings
words = ["python", "java", "javascript", "php", "go"]
long_words = list(filter(lambda word: len(word) > 4, words))
print(long_words) # ['python', 'javascript']
Combining Map and Filter
python
# Process data pipeline
numbers = [-5, -2, 0, 3, 7, -1, 9]
# Filter positive numbers, then square them
positive_squares = list(map(lambda x: x**2,
filter(lambda x: x > 0, numbers)))
print(positive_squares) # [9, 49, 81]
# More readable with intermediate steps
positive_nums = filter(lambda x: x > 0, numbers)
squared_nums = map(lambda x: x**2, positive_nums)
result = list(squared_nums)
# Using list comprehension (often more readable)
result_comp = [x**2 for x in numbers if x > 0]
print(result_comp) # [9, 49, 81]
Inner/Nested Functions
Basic Nested Functions
python
def outer_function(text):
def inner_function():
print(f"Inner function says: {text}")
print("Outer function executing")
inner_function() # Call inner function
return inner_function # Can return inner function
# Usage
outer_function("Hello from inner!")
Closures - Inner Functions with State
python
def create_counter(initial=0):
count = initial
def increment():
nonlocal count
count += 1
return count
def decrement():
nonlocal count
count -= 1
return count
def get_count():
return count
# Return dictionary of functions
return {
'increment': increment,
'decrement': decrement,
'get_count': get_count
}
# Create counter instances
counter1 = create_counter(10)
counter2 = create_counter(0)
print(counter1['increment']()) # 11
print(counter1['increment']()) # 12
print(counter2['increment']()) # 1
print(counter1['get_count']()) # 12
print(counter2['get_count']()) # 1
Decorators Using Nested Functions
python
def timer_decorator(func):
import time
def wrapper(*args, **kwargs):
start_time = [Link]()
result = func(*args, **kwargs)
end_time = [Link]()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds")
return result
return wrapper
# Using decorator
@timer_decorator
def slow_function():
import time
[Link](1)
return "Function completed"
result = slow_function() # Prints timing information
# Manual decorator application
def fast_function():
return sum(range(1000))
timed_fast_function = timer_decorator(fast_function)
result = timed_fast_function()
Factory Functions (Functions that Return Functions)
python
def create_multiplier(factor):
"""Factory function that creates multiplier functions"""
def multiplier(number):
return number * factor
return multiplier
# Create specialized functions
double = create_multiplier(2)
triple = create_multiplier(3)
times_ten = create_multiplier(10)
print(double(5)) # 10
print(triple(4)) # 12
print(times_ten(3)) # 30
# Validator factory
def create_validator(min_val, max_val):
def validate(value):
return min_val <= value <= max_val
return validate
age_validator = create_validator(0, 120)
grade_validator = create_validator(0, 100)
print(age_validator(25)) # True
print(grade_validator(105)) # False
Advanced Function Concepts
Function Annotations and Type Hints
python
def calculate_area(length: float, width: float) -> float:
"""Calculate rectangle area with type hints"""
return length * width
def process_data(items: list, multiplier: int = 2) -> list:
"""Process list items with optional multiplier"""
return [item * multiplier for item in items]
Recursive Functions
python
def factorial(n):
"""Calculate factorial using recursion"""
if n <= 1:
return 1
return n * factorial(n - 1)
def fibonacci(n):
"""Calculate nth Fibonacci number"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# Usage
print(factorial(5)) # 120
print(fibonacci(7)) # 13
# Optimized recursive function with memoization
def fibonacci_memo(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci_memo(n-1, memo) + fibonacci_memo(n-2, memo)
return memo[n]
Higher-Order Functions
python
def apply_operation(func, numbers):
"""Apply function to list of numbers"""
return [func(num) for num in numbers]
def square(x):
return x ** 2
def cube(x):
return x ** 3
numbers = [1, 2, 3, 4, 5]
squared_nums = apply_operation(square, numbers) # [1, 4, 9, 16, 25]
cubed_nums = apply_operation(cube, numbers) # [1, 8, 27, 64, 125]
# Function as argument
def process_data(data, transform_func, filter_func):
"""Process data with transformation and filtering"""
filtered = filter(filter_func, data)
transformed = map(transform_func, filtered)
return list(transformed)
data = [-3, -1, 0, 2, 5, -2, 8]
result = process_data(data,
transform_func=lambda x: x**2,
filter_func=lambda x: x > 0)
print(result) # [4, 25, 64]
Practical Examples
Real-World Function Applications
python
# Data processing pipeline
def clean_data(raw_data):
"""Clean and validate input data"""
def is_valid_record(record):
return len(record) >= 3 and all([Link]() for field in record)
def clean_record(record):
return [[Link]().title() for field in record]
# Filter valid records and clean them
valid_records = filter(is_valid_record, raw_data)
cleaned_records = map(clean_record, valid_records)
return list(cleaned_records)
# Sample data
raw_student_data = [
[" alice johnson ", " 25 ", " computer science "],
["bob smith", "30", ""], # Invalid - empty field
[" charlie brown ", " 22 ", " mathematics "],
["", "25", "physics"] # Invalid - empty name
]
clean_students = clean_data(raw_student_data)
print(clean_students)
# [['Alice Johnson', '25', 'Computer Science'],
# ['Charlie Brown', '22', 'Mathematics']]
# Calculator with nested functions
def create_calculator():
def add(x, y): return x + y
def subtract(x, y): return x - y
def multiply(x, y): return x * y
def divide(x, y): return x / y if y != 0 else "Cannot divide by zero"
def calculate(operation, x, y):
operations = {
'+': add, '-': subtract,
'*': multiply, '/': divide
}
if operation in operations:
return operations[operation](x, y)
return "Invalid operation"
return calculate
calc = create_calculator()
print(calc('+', 10, 5)) # 15
print(calc('*', 4, 3)) # 12
print(calc('/', 10, 0)) # Cannot divide by zero
Callback Functions and Event Handling
python
def process_numbers(numbers, success_callback, error_callback):
"""Process numbers with callback functions"""
results = []
for num in numbers:
try:
if isinstance(num, (int, float)):
result = num ** 2
[Link](result)
success_callback(num, result)
else:
error_callback(num, "Not a number")
except Exception as e:
error_callback(num, str(e))
return results
# Callback functions
def on_success(original, result):
print(f"✓ {original}² = {result}")
def on_error(value, error_msg):
print(f"✗ Error with {value}: {error_msg}")
# Usage
mixed_data = [2, 3, "hello", 4, None, 5]
results = process_numbers(mixed_data, on_success, on_error)
Function Documentation and Best Practices
Docstrings and Documentation
python
def calculate_bmi(weight, height):
"""
Calculate Body Mass Index (BMI).
Args:
weight (float): Weight in kilograms
height (float): Height in meters
Returns:
float: BMI value
Raises:
ValueError: If weight or height is not positive
Example:
>>> calculate_bmi(70, 1.75)
22.86
"""
if weight <= 0 or height <= 0:
raise ValueError("Weight and height must be positive")
bmi = weight / (height ** 2)
return round(bmi, 2)
# Access docstring
print(calculate_bmi.__doc__)
help(calculate_bmi) # Shows formatted help
Function Best Practices
python
# Single Responsibility Principle
def read_file(filename):
"""Only reads file - single responsibility"""
with open(filename, 'r') as file:
return [Link]()
def process_text(text):
"""Only processes text - single responsibility"""
return [Link]().upper()
def save_result(result, filename):
"""Only saves result - single responsibility"""
with open(filename, 'w') as file:
[Link](result)
# Pure functions (no side effects)
def calculate_tax(amount, rate):
"""Pure function - same input always gives same output"""
return amount * rate
# Avoid global state in functions when possible
def impure_function():
global some_global_var
some_global_var += 1 # Side effect - modifies global state
def pure_function(current_value):
return current_value + 1 # No side effects
Error Handling in Functions
python
def safe_divide(a, b):
"""Safely divide two numbers with error handling"""
try:
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both arguments must be numbers")
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
except TypeError as e:
return f"Type Error: {e}"
except ValueError as e:
return f"Value Error: {e}"
except Exception as e:
return f"Unexpected error: {e}"
# Usage
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # Value Error: Cannot divide by zero
print(safe_divide("10", 2)) # Type Error: Both arguments must be numbers
Quick Reference Summary
Function Types
Regular Functions: def function_name():
Lambda Functions: lambda args: expression
Nested Functions: Functions inside other functions
Generator Functions: Functions with yield
Scope Rules (LEGB)
1. Local: Inside current function
2. Enclosing: In outer function (for nested functions)
3. Global: Module level
4. Built-in: Python built-in names
Key Built-in Functions
map(): Apply function to each element
filter(): Filter elements based on condition
enumerate(): Get index-value pairs
zip(): Combine multiple iterables
sorted(): Sort with custom key function
Best Practices
Use descriptive function names
Write clear docstrings
Keep functions small and focused
Prefer pure functions when possible
Use type hints for better code documentation
Handle errors appropriately
Master functions and scope for writing clean, reusable Python code