-2
def pass_thru(func_to_decorate):
    def new_func(*args, **kwargs):  #1
        print("Function has been decorated.  Congratulations.")
        # Do whatever else you want here
        return func_to_decorate(*args, **kwargs)  #2
    return new_func


def print_args(*args):
    for arg in args:
        print(arg)


a = pass_thru(print_args)
a(1,2,3)

>> Function has been decorated.  Congratulations.
1
2
3

I understand that *args is used in #1 since it is a function declaration. But why is it necessary to write *args in #2 even if it not a function declaration?

Jin
  • 1,902
  • 3
  • 15
  • 26
  • 1
    You're bundling up the positional arguments into the tuple of args, then unbundling them back into separate arguments when you call the wrapped function. – jonrsharpe Sep 14 '17 at 08:17

1 Answers1

1

When used in function declaration, *args turn positional arguments into tuple:

def foo(a, *args):
    pass
foo(1, 2, 3)    #  a = 1, args = (2, 3)

When used in function call, *args expands tuple into positional arguments:

def bar(a, b, c):
    pass
args = (2, 3)
foo(1, *args)   # a = 1, b = 2, c = 3

This is two opposite processes, so combining them allows to pass arbitary number of arguments to decorated function.

@pass_thru
def foo(a, b):
    pass
@pass_thru
def bar(a, b, c):
    pass

foo(1, 2)     # -> args = (1, 2) -> a = 1, b = 2
bar(1, 2, 3)  # -> args = (1, 2, 3) -> a = 1, b = 2, c = 3
myaut
  • 11,174
  • 2
  • 30
  • 62