0% found this document useful (0 votes)
2 views30 pages

Python Functions and Scope Guide

This document provides a comprehensive reference on Python functions, covering topics such as function definitions, parameters, variable scope, lambda expressions, map and filter functions, and advanced concepts like closures and decorators. It includes practical examples and best practices for writing and documenting functions. The content is structured to guide users from basic to advanced function usage in Python.

Uploaded by

S SA
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views30 pages

Python Functions and Scope Guide

This document provides a comprehensive reference on Python functions, covering topics such as function definitions, parameters, variable scope, lambda expressions, map and filter functions, and advanced concepts like closures and decorators. It includes practical examples and best practices for writing and documenting functions. The content is structured to guide users from basic to advanced function usage in Python.

Uploaded by

S SA
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

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

You might also like