python

examples

examples.py🐍
"""
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)
Examples - Python Tutorial | DeepML