python
examples
examples.py🐍python
"""
10 - Advanced Functions: Examples
Run this file to see advanced function concepts in action!
"""
print("=" * 60)
print("ADVANCED FUNCTIONS - EXAMPLES")
print("=" * 60)
# =============================================================================
# 1. CLOSURES
# =============================================================================
print("\n--- 1. Closures ---\n")
def make_multiplier(factor):
"""Create a function that multiplies by factor."""
def multiplier(x):
return x * factor
return multiplier
# Create closures
double = make_multiplier(2)
triple = make_multiplier(3)
print(f"double(5): {double(5)}")
print(f"triple(5): {triple(5)}")
# Counter closure
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter1 = make_counter()
counter2 = make_counter()
print(f"counter1(): {counter1()}")
print(f"counter1(): {counter1()}")
print(f"counter2(): {counter2()}") # Independent!
# =============================================================================
# 2. BASIC DECORATORS
# =============================================================================
print("\n--- 2. Basic Decorators ---\n")
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
@uppercase
def greet(name):
return f"hello, {name}"
print(greet("alice"))
# =============================================================================
# 3. DECORATOR WITH ARGUMENTS
# =============================================================================
print("\n--- 3. Decorator with Arguments ---\n")
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator
@repeat(times=3)
def say_hi():
return "Hi!"
print(say_hi())
# =============================================================================
# 4. PRESERVING FUNCTION METADATA
# =============================================================================
print("\n--- 4. Preserving Metadata with @wraps ---\n")
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper docstring."""
return func(*args, **kwargs)
return wrapper
@my_decorator
def my_function():
"""My function's docstring."""
pass
print(f"Function name: {my_function.__name__}")
print(f"Function doc: {my_function.__doc__}")
# =============================================================================
# 5. PRACTICAL DECORATORS
# =============================================================================
print("\n--- 5. Practical Decorators ---\n")
import time
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end-start:.4f}s")
return result
return wrapper
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@timer
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(f"fibonacci(30) = {fibonacci(30)}")
# =============================================================================
# 6. CLASS-BASED DECORATOR
# =============================================================================
print("\n--- 6. Class-Based Decorator ---\n")
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Call #{self.count} of {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
say_hello()
# =============================================================================
# 7. BASIC GENERATORS
# =============================================================================
print("\n--- 7. Basic Generators ---\n")
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
print("Counting to 5:")
for num in count_up_to(5):
print(num, end=" ")
print()
# Manual iteration
gen = count_up_to(3)
print(f"\nnext(gen): {next(gen)}")
print(f"next(gen): {next(gen)}")
print(f"next(gen): {next(gen)}")
# =============================================================================
# 8. GENERATOR EXPRESSION
# =============================================================================
print("\n--- 8. Generator Expression ---\n")
# List comprehension (creates list in memory)
squares_list = [x**2 for x in range(5)]
print(f"List: {squares_list}")
# Generator expression (lazy evaluation)
squares_gen = (x**2 for x in range(5))
print(f"Generator: {squares_gen}")
print(f"List from generator: {list(squares_gen)}")
# =============================================================================
# 9. GENERATOR PIPELINE
# =============================================================================
print("\n--- 9. Generator Pipeline ---\n")
def numbers(n):
for i in range(n):
yield i
def square(nums):
for n in nums:
yield n ** 2
def filter_even(nums):
for n in nums:
if n % 2 == 0:
yield n
# Chain generators
pipeline = filter_even(square(numbers(10)))
print(f"Even squares from 0-9: {list(pipeline)}")
# =============================================================================
# 10. YIELD FROM
# =============================================================================
print("\n--- 10. yield from ---\n")
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
nested = [1, [2, 3, [4, 5]], 6, [7, 8]]
print(f"Flattened: {list(flatten(nested))}")
# =============================================================================
# 11. ITERATOR CLASS
# =============================================================================
print("\n--- 11. Iterator Class ---\n")
class Squares:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current > self.end:
raise StopIteration
result = self.current ** 2
self.current += 1
return result
print("Squares from 1 to 5:")
for sq in Squares(1, 5):
print(sq, end=" ")
print()
# =============================================================================
# 12. MAP, FILTER, REDUCE
# =============================================================================
print("\n--- 12. map, filter, reduce ---\n")
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# map: apply function to all
doubled = list(map(lambda x: x * 2, numbers))
print(f"Doubled: {doubled}")
# filter: keep matching elements
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Evens: {evens}")
# reduce: accumulate values
total = reduce(lambda x, y: x + y, numbers)
print(f"Sum: {total}")
product = reduce(lambda x, y: x * y, numbers)
print(f"Product: {product}")
# =============================================================================
# 13. SORTED WITH KEY
# =============================================================================
print("\n--- 13. sorted with key ---\n")
words = ['banana', 'apple', 'Cherry', 'date']
# Sort by length
by_length = sorted(words, key=len)
print(f"By length: {by_length}")
# Sort case-insensitive
by_lower = sorted(words, key=str.lower)
print(f"Case-insensitive: {by_lower}")
# Sort objects
students = [
{'name': 'Alice', 'grade': 85},
{'name': 'Bob', 'grade': 92},
{'name': 'Charlie', 'grade': 78},
]
by_grade = sorted(students, key=lambda x: x['grade'], reverse=True)
print(f"By grade (desc): {[s['name'] for s in by_grade]}")
# =============================================================================
# 14. PARTIAL FUNCTIONS
# =============================================================================
print("\n--- 14. Partial Functions ---\n")
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(f"square(5): {square(5)}")
print(f"cube(5): {cube(5)}")
# Practical use
def greet(greeting, name):
return f"{greeting}, {name}!"
say_hello = partial(greet, "Hello")
say_hi = partial(greet, "Hi")
print(say_hello("Alice"))
print(say_hi("Bob"))
# =============================================================================
# 15. FUNCTION COMPOSITION
# =============================================================================
print("\n--- 15. Function Composition ---\n")
def compose(*functions):
def inner(arg):
result = arg
for f in reversed(functions):
result = f(result)
return result
return inner
add_one = lambda x: x + 1
double = lambda x: x * 2
square = lambda x: x ** 2
# Execute right to left: add_one(3) -> double(4) -> square(8) = 64
composed = compose(square, double, add_one)
print(f"compose(square, double, add_one)(3) = {composed(3)}")
# Step by step: 3 -> 4 -> 8 -> 64
# =============================================================================
# 16. INFINITE GENERATOR
# =============================================================================
print("\n--- 16. Infinite Generator ---\n")
def infinite_counter(start=0):
n = start
while True:
yield n
n += 1
# Use with islice to limit
from itertools import islice
print("First 5 from infinite counter:")
for num in islice(infinite_counter(100), 5):
print(num, end=" ")
print()
print("\n" + "=" * 60)
print("END OF EXAMPLES")
print("=" * 60)