How can a function takes another function WITH arguments, as a parameter?

Question:

def Wrapper(func):
  def MyPrint(message):
    print('Hello ' + message)
  
  func(MyPrint)

Wrapper(lambda my_print: my_print('world'))

I’m confused of the above code. I get that a function can take another function as parameter and use func() to invoke it, but how can Wrapper(lambda my_print: my_print('world')) call the inner function MyPrint? The func is a parameter, and the lambda my_print: my_print('world') is its argument, but somehow the argument seems to utilizes (penatrate through) an inner function inside Wrapper.

This is a very common pattern in JavaScript but confuses me. I think an explaination of what happened in detail might help.

Asked By: Xin Jin

||

Answers:

Simplest answer:
You are renaming in the local scope of ‘Wrapper’ function.
Detailed answer:
When ‘Wrapper’ gets called, it takes the throw away function lambda my_print: my_print('World') (now becomes ‘func’ in ‘Wrapper’ local scope). The throw away function needs the variable ‘my_print’ to be a function or it’ll throw an error. That function must also take a variable itself, a preset string (‘World’). ‘MyPrint’ predefined in the local scope of ‘Wrapper’ function, gets renamed as ‘my_print’, which takes the preset argument ‘World’ string. When ‘func’ is called (in local scope of ‘Wrapper’), it return ‘World’ string as the argument for ‘my_print’ (which is ‘MyPrint’ just renamed) function. This prints ‘Hello World’ to the screen before the ‘Wrapper’ function closes out. When it closes, you are back in the global scope.

Answered By: DracoDaine

(lambda my_print: my_print('world') doesn’t call MyPrint directly; MyPrint is passed as its argument in func(MyPrint).

Try the substitution method:

Wrapper(lambda my_print: my_print('world'))

—>
func is bound to lambda my_print: my_print('world') in the body of Wrapper:

def MyPrint(message):
    print('Hello ' + message)
  
  (lambda my_print: my_print('world'))(MyPrint)

—>

my_print is bound to MyPrint in the body of the lambda:

def MyPrint(message):
    print('Hello ' + message)
  
  MyPrint('world')

Addendum: the same, but with a non-local function and no lambda, in case that makes it clearer:

def GreetingPrinter(message):
    print('Hello ' + message)

def Wrapper(func):
  func(GreetingPrinter)

def PrintWithWorld(printer):
    printer('World')

Wrapper(PrintWithWorld)
Answered By: molbdnilo
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.