Decorators Advanced

Decorator with Parameters

  1. # decorator factory
  2. def repeat(times):
  3. def decorator(func):
  4. def wrapper(*args):
  5. for _ in range(times):
  6. result = func(*args)
  7. return result
  8. return wrapper
  9. return decorator
  10. @repeat(times=3)
  11. def greet():
  12. print("Hello")
  13. greet()
  14. #ans: Hello (3 times)

Timing Decorator

  1. import time
  2. # measure execution time
  3. def timer(func):
  4. def wrapper(*args, **kwargs):
  5. start = time.time()
  6. result = func(*args, **kwargs)
  7. end = time.time()
  8. print(f"{func.__name__}: {end-start}s")
  9. return result
  10. return wrapper
  11. @timer
  12. def slow():
  13. time.sleep(1)

Caching Decorator

  1. from functools import wraps
  2. # simple cache
  3. def cache(func):
  4. cached = {}
  5. @wraps(func)
  6. def wrapper(*args):
  7. if args in cached:
  8. return cached[args]
  9. result = func(*args)
  10. cached[args] = result
  11. return result
  12. return wrapper
  13. @cache
  14. def fibonacci(n):
  15. if n < 2:
  16. return n
  17. return fibonacci(n-1) + fibonacci(n-2)

Authentication Decorator

  1. # check permission
  2. def require_auth(func):
  3. def wrapper(*args, **kwargs):
  4. if not is_authenticated():
  5. raise PermissionError()
  6. return func(*args, **kwargs)
  7. return wrapper
  8. @require_auth
  9. def sensitive_operation():
  10. return "Secret"

Exercises - Part 1

  1. # decorator with args?
  2. def repeat(n):
  3. def decorator(func):
  4. def wrapper():
  5. for _ in range(n):
  6. func()
  7. return wrapper
  8. return decorator
  9. @repeat(2)
  10. def func():
  11. print("Hi")
  12. func()
  13. #ans: Hi (twice)

Exercises - Part 2

  1. # lru_cache?
  2. from functools import lru_cache
  3. @lru_cache(maxsize=128)
  4. def fib(n):
  5. if n < 2:
  6. return n
  7. return fib(n-1) + fib(n-2)
  8. #ans: built-in caching

Exercises - Part 3

  1. # multiple decorators with args?
  2. @decorator1(arg1)
  3. @decorator2(arg2)
  4. def func():
  5. pass
  6. #ans: both decorators get args

Exercises - Part 4

  1. # decorator counting calls?
  2. def count_calls(func):
  3. def wrapper(*args):
  4. wrapper.calls += 1
  5. return func(*args)
  6. wrapper.calls = 0
  7. return wrapper
  8. @count_calls
  9. def func():
  10. pass
  11. func()
  12. func.calls
  13. #ans: 1

Exercises - Part 5

  1. # retry decorator?
  2. def retry(max_attempts):
  3. def decorator(func):
  4. def wrapper(*args):
  5. for attempt in range(max_attempts):
  6. try:
  7. return func(*args)
  8. except:
  9. if attempt == max_attempts-1:
  10. raise
  11. return wrapper
  12. return decorator

Exercises - Part 6

  1. # validation decorator?
  2. def validate(type_):
  3. def decorator(func):
  4. def wrapper(arg):
  5. if not isinstance(arg, type_):
  6. raise TypeError()
  7. return func(arg)
  8. return wrapper
  9. return decorator
  10. @validate(int)
  11. def double(x):
  12. return x * 2

Exercises - Part 7

  1. # property as decorator?
  2. class MyClass:
  3. @property
  4. def value(self):
  5. return 5
  6. #ans: property is a decorator

Exercises - Part 8

  1. # classmethod as decorator?
  2. class MyClass:
  3. @classmethod
  4. def method(cls):
  5. pass
  6. #ans: classmethod is a decorator

Exercises - Part 9

  1. # staticmethod as decorator?
  2. class MyClass:
  3. @staticmethod
  4. def method():
  5. pass
  6. #ans: staticmethod is a decorator

Exercises - Part 10

  1. # decorator preserving signature?
  2. from functools import wraps
  3. def decorator(func):
  4. @wraps(func)
  5. def wrapper(*args, **kwargs):
  6. return func(*args, **kwargs)
  7. return wrapper
  8. #ans: preserves func signature

Google tag (gtag.js)