0

This question relates to this video: https://www.youtube.com/watch?v=jXugs4B3lwU

The piece I'm missing is somewhat simpler than the concept the video is covering overall. In the example code below: Why does the inner function (inner()) pick up the value of the argument that is passed in function call of f and g? (i.e. f("y arg"), g("other y arg"))

To my untrained eye, the level_five function only has one parameter defined ('n') which is referenced in the variable assignment to 'z' under the level_five function definition. At no point is a parameter called 'y' defined in the outer (level_five) function definition to be passed to inner()....so how does inner() implicitly pick up the passed value as it's y parameter when the outer function is being called?

I feel like it's because when the function is assigned to f or g, the first positional argument has been defined (i.e. this value goes to parameter 'n' of level_five when f or g are called), and then the argument supplied to f or g when they are called, is somehow mysteriously picked up using as a second positional parameter as though there is an implicit *args in the level_five definition, and is thus making its way to inner()....I've managed to confuse myself on exactly how this is happening, can anyone clarify this?

x = "global x"    

def level_five(n):
    z = f"outer z {n}"

    def inner(y):
        return x, y, z

    return inner

def main():
    f = level_five(0)
    g =  level_five(1)
    print(f("y arg"), g("other y arg"))

main()
4
  • 3
    These are called closures; this question has some answers specific to Python, while this wikipedia article covers the general topic. Commented Jun 8, 2024 at 2:31
  • @larsks okay, thanks that terminology is helping me find ways to understand it. Still not quite clear on how it determines which arg ends up going to which parameter in the definition, I presume it just sort operates top to bottom and left to right per line assigning as positional arguments, until everything has a value or runs out of values and raises an exception? Commented Jun 8, 2024 at 5:08
  • Ah thanks somehow that has flicked the light bulb switch a bit, if this reasoning is somewhat correct. Basically calling level_five is returning inner() as a whole function that is just being stored in the variable, and when that returned function (inner) was defined it already has the other variables of the tuple that it returns defined (that's the closure part), and hence there is only the one positional parameter left (y) per it's definition, and so it gets the one argument that is passed when calling f or g...that about right? Commented Jun 8, 2024 at 6:14
  • Okay, a batch of reading later, this making more sense to me. Didn't realise you could pass a whole function, let alone a 'wrapped and partially prepared' one! Thanks all. Commented Jun 8, 2024 at 12:01

1 Answer 1

1

When you run the level_five(0), it returns a function and stores it in f. As the function has not run yet, there was no need to pass any argument (y) to the function (inner). The function stored in f behaves like:

def f(y):

   return 0 , y , "outer z 0"

and then you can simply run f as you did in the print statement.

Sign up to request clarification or add additional context in comments.

1 Comment

So, calling level_five results in returning inner(), which gets stored in a variable (f and g), but when it was returned two of the values for the tuple of inner() are already defined (that's the closure part), and hence there is only the one positional parameter left, per the func def of inner (namely 'y'), and hence how/why the arg that is passed when calling f or g ends up being passed positionally to the y parameter of inner (which in this, case happens to be the only positional arg of the closure)...that about right?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.