Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the equivalent of decorators with arguments without the syntactical-sugar?

I'm learning about decorators and came across an example where the decorator took an argument. This was a little confusing for me though, because I learned that (note: the examples from this question are mostly from this article):

def my_decorator(func):
  def inner(*args, **kwargs):
    print('Before function runs')
    func(*args, **kwargs)
    print('After function ran')
  return inner

@my_decorator
def foo(thing_to_print):
  print(thing_to_print)

foo('Hello')
# Returns:
# Before function runs
# Hello
# After function ran

was equivalent to

foo = my_wrapper(foo)

So, it doesn't make sense to me how something could take an argument, to better explain, here is a decorator example that takes an argument:

def repeat(num_times):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat

@repeat(num_times=4)
def greet(name):
    print(f"Hello {name}")

greet('Bob')
# Returns:
# Hello Bob
# Hello Bob
# Hello Bob
# Hello Bob

So when I see this I am thinking:

greet = repeat(greet, num_times=4)

I know that that can't be right because num_times is the only argument that should be getting passed. So what is the correct equivalent to @repeat(num_times=4) without the "@-symbol-syntax"? Thanks!

like image 228
ThatComputerGuy Avatar asked Apr 11 '20 15:04

ThatComputerGuy


1 Answers

In this case it would be:

greet = repeat(num_times=4)(greet)

This would explain the two levels of nesting within repeat (you need to call the function "twice," you could say). repeat(num_times=4) returns a decorator, then that decorator wraps around greet.

like image 133
iz_ Avatar answered Oct 08 '22 08:10

iz_