Copy docker run --rm zchencow/innozverse-python:latest python3 -c "
import functools
import time
import threading
from contextlib import contextmanager
# Step 3: Caching decorators
@functools.lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
if n <= 1: return n
return fibonacci(n-1) + fibonacci(n-2)
start = time.perf_counter()
print('fib(40):', fibonacci(40))
print(f'Time: {(time.perf_counter()-start)*1000:.2f}ms')
print('Cache info:', fibonacci.cache_info())
# Custom TTL cache
def ttl_cache(seconds: float):
def decorator(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
now = time.monotonic()
if args in cache:
result, ts = cache[args]
if now - ts < seconds:
print(f' [ttl-cache] hit for {args}')
return result
result = func(*args)
cache[args] = (result, now)
return result
wrapper.cache_clear = lambda: cache.clear()
return wrapper
return decorator
@ttl_cache(seconds=1.0)
def fetch_price(product_id: int) -> float:
print(f' [fetch] fetching price for #{product_id}')
return {1: 864.0, 2: 49.99, 3: 99.99}.get(product_id, 0)
print('Price:', fetch_price(1))
print('Price (cached):', fetch_price(1))
# Step 4: Class-based decorator (stateful)
class CountCalls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.call_count = 0
self._lock = threading.Lock()
def __call__(self, *args, **kwargs):
with self._lock:
self.call_count += 1
return self.func(*args, **kwargs)
def reset(self):
self.call_count = 0
@CountCalls
def process(x: int) -> int:
return x * 2
for i in range(5): process(i)
print(f'process called {process.call_count} times')
# Step 5: Logging decorator
def log_calls(logger=print, level='INFO'):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
arg_str = ', '.join([repr(a) for a in args] + [f'{k}={v!r}' for k,v in kwargs.items()])
logger(f'[{level}] CALL {func.__name__}({arg_str})')
try:
result = func(*args, **kwargs)
logger(f'[{level}] RETURN {func.__name__} → {result!r}')
return result
except Exception as e:
logger(f'[ERROR] {func.__name__} raised {type(e).__name__}: {e}')
raise
return wrapper
return decorator
@log_calls(level='DEBUG')
def divide(a: float, b: float) -> float:
if b == 0: raise ZeroDivisionError('Cannot divide by zero')
return a / b
divide(10, 3)
try: divide(1, 0)
except ZeroDivisionError: pass
# Step 6: Context manager as decorator
@contextmanager
def transaction(name: str):
print(f'[txn] BEGIN {name}')
try:
yield
print(f'[txn] COMMIT {name}')
except Exception as e:
print(f'[txn] ROLLBACK {name}: {e}')
raise
with transaction('update_prices'):
print(' Updating price...')
try:
with transaction('bad_update'):
raise ValueError('constraint violation')
except ValueError:
pass
# Step 7: Combine decorators — decorator stacking
def require_auth(func):
@functools.wraps(func)
def wrapper(*args, user=None, **kwargs):
if not user:
raise PermissionError('Authentication required')
return func(*args, user=user, **kwargs)
return wrapper
def audit_log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'[audit] {func.__name__} called')
return func(*args, **kwargs)
return wrapper
@audit_log
@require_auth
@timer_decorator
def delete_product(product_id: int, user=None):
return f'Deleted product #{product_id} by {user}'
# Step 8: Capstone — pipeline decorator
def pipeline(*decorators):
def decorator(func):
for dec in reversed(decorators):
func = dec(func)
return func
return decorator
def timer_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
s = time.perf_counter()
r = func(*args, **kwargs)
print(f'[time] {func.__name__}: {(time.perf_counter()-s)*1000:.1f}ms')
return r
return wrapper
standard = pipeline(
timer_decorator,
log_calls(level='INFO'),
retry(max_attempts=2, delay=0.001),
)
@standard
def api_call(endpoint: str) -> dict:
return {'endpoint': endpoint, 'status': 200}
result = api_call('/api/products')
print('Final result:', result)
"