This is an archived version of the course and is no longer updated. Please find the latest version of the course on the main webpage.

Decorating functions with parameters

Can we decorate a function that has some parameters?

Of course we can. Just include the same parameters in the inner (wrapper) function.

def validator(func):
    def wrapper(numerator, denominator):
        try:
            return func(numerator, denominator)
        except ZeroDivisionError:
            print("Sorry, I do not know how to divide by 0.")
            return float("NaN")
    
    return wrapper

@validator
def divide(numerator, denominator):
    return numerator/denominator

answer = divide(5, 0)
print(answer)

What if you don’t care about tracking the parameters? Just use *args and **kwargs in the inner function, and pass them on to the function you are wrapping.

def repeat(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        func(*args, **kwargs)
    
    return wrapper

@repeat
def greet(message, firstname, lastname):
    print(f"{message}, {firstname} {lastname}!")

greet("Merry Christmas", "Josiah", "Wang")

And what if our decorator itself needs some parameters? For example, I might want to allow the decorator to repeat a function an arbitrary number of times. Well, that requires a bit more work. You will have to wrap your decorator further in another function. Don’t worry too much about this one - you will unlikely need to do this! (And my head is spinning too!)

def n_repeat(times):
    def repeat(func):
        def wrapper(*args, **kwargs):
            for i in range(times):
                func(*args, **kwargs)
        return wrapper
    return repeat

@n_repeat(5)
def greet(message, firstname, lastname):
    print(f"{message}, {firstname} {lastname}!")

@n_repeat(3)
def sing(line):
    print(line)

sing("We wish you a Merry Christmas...")
greet("Happy New Year", "Josiah", "Wang")

print(sing)  
## <function n_repeat.<locals>.repeat.<locals>.wrapper at 0x7f9e0198c430>