PythiloLesson 7 of 9 · progress kept in this browser

Decorators

A decorator takes a function and returns a replacement, which then runs in its place on every call. @name above a def is shorthand for f = name(f).

from functools import wraps

def shout(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return result.upper()
    return wrapper

@shout
def hello(name):
    return f"hi {name}"
  • *args, **kwargs lets the wrapper accept whatever the original accepts.
  • @wraps(fn) copies the name and docstring, which keeps tracebacks and tools readable.
Your task

Write a decorator count_calls that stores how many times the function ran in a .calls attribute, starting at 0. Write a decorator factory retry(times) that calls the function up to times times in total, returning the first success and re-raising the last exception if all fail. Both keep the function's name.