Functional Utilities: map()
, filter()
, reduce()
, any()
, all()
✅ map()
– Apply a Function to Each Element
nums = [1, 2, 3] squared = list(map(lambda x: x**2, nums)) print(squared) # [1, 4, 9]
✅ filter()
– Extract Elements Based on Condition
nums = [-2, -1, 0, 1, 2] positives = list(filter(lambda x: x > 0, nums)) print(positives) # [1, 2]
✅ reduce()
– Cumulative Computation (From functools
)
global local variables nested functions
from functools import reduce nums = [1, 2, 3, 4] product = reduce(lambda x, y: x * y, nums) print(product) # 24
✔ Use Case: Aggregation (sum, product, concatenation).
✅ any()
– Checks if At Least One Condition is True
nums = [-1, 0, 2] print(any(x > 0 for x in nums)) # True
✅ all()
– Checks if All Conditions Are True
nums = [1, 2, 3] print(all(x > 0 for x in nums)) # True
✅ sorted()
– Sort an Iterable
sorted()
returns a new sorted list from an iterable without modifying the original.
nums = [3, 1, 4, 2] sorted_nums = sorted(nums) print(sorted_nums) # [1, 2, 3, 4]
- Supports custom sorting with key
- Use reverse=True for descending order
words = ["banana", "kiwi", "apple"] sorted_words = sorted(words, key=len) # Sort by length print(sorted_words) # ['kiwi', 'apple', 'banana']
Scope & Lifetime in Python
✅ Local vs. Global Scope
- Local: Variables inside a function (only accessible within that function).
- Global: Variables declared at the top level of a module (accessible everywhere).
x = 10 # Global variable def func(): x = 5 # Local variable (does not modify global x) print(x) # 5 func() print(x) # 10 (global x remains unchanged)
✅ global
Keyword
x = 10 def update(): global x x += 5 # Modifies the global variable update() print(x) # 15
✅ nonlocal
Keyword (For Nested Functions)
def outer(): x = 10 def inner(): nonlocal x x += 5 inner() print(x) # 15 outer()
✅ Variable Shadowing
A local variable with the same name as a global variable "shadows" it inside a function.
x = "global" def shadow(): x = "local" # This does not change the global x print(x) # "local" shadow() print(x) # "global"
Function Parameters & Arguments
✅ Positional Arguments
def greet(name, age): print(f"{name} is {age} years old.") greet("Alice", 25) # Alice is 25 years old.
✅ Keyword Arguments
greet(age=30, name="Bob") # Bob is 30 years old.
✅ Default Values
def greet(name, message="Hello"): print(f"{message}, {name}!") greet("Charlie") # Hello, Charlie! greet("David", "Hi") # Hi, David!
✅ *args
(Variable Positional Arguments)
def add(*numbers): return sum(numbers) print(add(1, 2, 3, 4)) # 10
✅ **kwargs
(Variable Keyword Arguments)
def info(**details): for key, value in details.items(): print(f"{key}: {value}") info(name="Emma", age=28, city="NY") # name: Emma, age: 28, city: NY
First-Class Functions in Python
Python treats functions as first-class citizens, meaning they can be assigned to variables, passed as arguments, and returned from functions.
✅ Assigning Functions to Variables
def greet(name): return f"Hello, {name}!" say_hello = greet # Assign function to variable print(say_hello("Alice")) # Hello, Alice!
✅ Passing Functions as Arguments
def shout(text): return text.upper() def whisper(text): return text.lower() def speak(func, message): return func(message) print(speak(shout, "hello")) # HELLO print(speak(whisper, "HELLO")) # hello
✅ Returning Functions from Functions
def multiplier(factor): def multiply(number): return number * factor return multiply # Returning the inner function double = multiplier(2) # Create a function that doubles numbers print(double(5)) # 10
Lambda Expressions in Python
✅ Syntax & Limitations
add = lambda x, y: x + y print(add(3, 5)) # 8
✅ Use Cases
- Sorting with
lambda
(Custom Key Function)
names = ["Alice", "Bob", "Charlie"] names.sort(key=lambda name: len(name)) print(names) # ['Bob', 'Alice', 'Charlie']
- Filtering with
filter()
nums = [1, 2, 3, 4, 5] evens = list(filter(lambda x: x % 2 == 0, nums)) print(evens) # [2, 4]
- Mapping with
map()
nums = [1, 2, 3] squared = list(map(lambda x: x**2, nums)) print(squared) # [1, 4, 9]
Decorators – Enhancing Functions Dynamically
✅ Basic Decorator Pattern
def decorator(func): def wrapper(): print("Before function call") func() print("After function call") return wrapper @decorator # Applying the decorator def say_hello(): print("Hello!") say_hello()
Output
Before function call Hello! After function call
✅ @decorator Syntax (Shortcut for Decorating)
Instead of manually wrapping:
say_hello = decorator(say_hello) # Manual decoration
We use @decorator to apply it directly.
✅ Stacking Multiple Decorators
Decorators are applied from top to bottom.
def uppercase(func): def wrapper(): return func().upper() return wrapper def exclaim(func): def wrapper(): return func() + "!!!" return wrapper @uppercase @exclaim def greet(): return "hello" print(greet()) # HELLO!!!
✅ Using functools.wraps to Preserve Function Metadata
Without wraps, the function name and docstring are lost.
from functools import wraps def decorator(func): @wraps(func) # Preserves original function metadata def wrapper(*args, **kwargs): print(f"Calling {func.__name__}") return func(*args, **kwargs) return wrapper @decorator def greet(name): """Greets a person.""" return f"Hello, {name}!" print(greet.__name__) # greet (not wrapper) print(greet.__doc__) # Greets a person.
✅ Decorators with Arguments
To pass arguments, nest an extra function layer.
def repeat(n): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator @repeat(3) # Runs function 3 times def say_hello(): print("Hello!") say_hello()
Closures – Functions That Remember Their Enclosing Scope
A closure is a function defined inside another function that "remembers" variables from its enclosing scope, even after the outer function has finished executing.
✅ Functions That Remember Enclosing Scope
def outer(x): def inner(y): return x + y # `inner` remembers `x` from `outer` return inner add_five = outer(5) # Returns a function that adds 5 print(add_five(3)) # 8
Even after outer(5) has executed, inner() still remembers x = 5.
✅ Use Cases of Closures
- Delayed Execution (Creating Function Templates)
def multiplier(n): def multiply(x): return x * n # `n` is remembered return multiply double = multiplier(2) triple = multiplier(3) print(double(5)) # 10 print(triple(5)) # 15
- Encapsulation (Data Hiding Without Classes)
def counter(): count = 0 # Hidden variable def increment(): nonlocal count # Modify the enclosed `count` count += 1 return count return increment counter1 = counter() print(counter1()) # 1 print(counter1()) # 2
count
is protected from external access but persists across function calls.
Recursion – Functions Calling Themselves
✅ Recursive Function Structure
def recurse(n): if n == 0: # Base case return recurse(n - 1) # Recursive call
- Base case stops infinite recursion.
- Each call adds a new stack frame, leading to stack overflow if unchecked.
✅ Factorial Using Recursion
def factorial(n): return 1 if n == 0 else n * factorial(n - 1) print(factorial(5)) # 120
✅ Fibonacci Using Recursion
def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2) print(fibonacci(6)) # 8
Recursive Fibonacci is inefficient; use memoization or iteration for performance.
✅ Tail Recursion (Conceptual, Not Optimized in Python)
Tail recursion eliminates extra stack frames, but Python does not optimize it.
def tail_factorial(n, acc=1): return acc if n == 0 else tail_factorial(n - 1, acc * n) print(tail_factorial(5)) # 120
- Python does not optimize tail recursion, so it still consumes stack space.
- Use recursion wisely; prefer iteration for deep recursive problems!
Introspection in Python
Introspection allows examining objects at runtime, including functions, classes, and modules.
✅ Basic Function Introspection
Python functions store metadata in special attributes.
def greet(name: str) -> str: """Returns a greeting message.""" return f"Hello, {name}!" print(greet.__name__) # greet print(greet.__doc__) # Returns a greeting message. print(greet.__annotations__) # {'name': <class 'str'>, 'return': <class 'str'>}
-
__name__
– Function name -
__doc__
– Docstring -
__annotations__
– Type hints
✅ Using the inspect Module for Advanced Inspection
The inspect
module retrieves detailed function metadata.
import inspect def example(x, y=10): """An example function.""" return x + y print(inspect.signature(example)) # (x, y=10) print(inspect.getsource(example)) # Function source code print(inspect.getdoc(example)) # Docstring print(inspect.getmodule(example)) # Module where it's defined
- inspect.signature(func) – Retrieves function parameters.
- inspect.getsource(func) – Gets function source code.
- inspect.getdoc(func) – Fetches the docstring.
- inspect.getmodule(func) – Returns the module name.
✅ Introspection helps with debugging, metaprogramming, and documentation!
Top comments (0)