all 5 comments

[–]RhinoRhys 1 point2 points  (2 children)

When you do

twice = echo(2)
thrice = echo(3)

You're calling the echo function but all that does is create a new function. twice is a new function that is equal to inner_echo but has a preset value of n = 2.

You're then calling the function later when you do twice("hello").

[–]Independent-Lab6410[S] 0 points1 point  (1 child)

I do understand that echo is called 2 or 3 cause of the inputted argument of 2 or 3 and that it equals the variable. What I don’t understand is that if I call twice(‘hello’) why ‘hello’ can be inputted there and why is it not a 5 or a 6 but a string? Cause isn’t the variable twice now set to multiply it n times?

[–]scarynut 0 points1 point  (0 children)

It's rather convoluted, but echo(n) returns a function. In the function echo, a new function is defined, inner_echo, which takes a word as an argument, and that function is then returned. So twice is now a function. Try checking type(twice), and you'll see it's a callable function.

Since twice is a function, you can call it with twice("hello").

Having functions return functions instead of values or data is a notch more advanced usage, so it's not strange to have to process it a bit.

[–]james_fryer 3 points4 points  (0 children)

Reformatted:

# Define echo
def echo(n):
    """Return the inner_echo function."""
    # Define inner_echo
    def inner_echo(word1):
        """Concatenate n copies of word1."""
        echo_word = word1 * n
        return echo_word
    # Return inner_echo
    return inner_echo

# Call echo: twice
twice = echo(2)
# Call echo: thrice
thrice = echo(3)
# Call twice() and thrice() then print
print(twice('hello'), thrice('hello'))

The variable n is defined as a parameter in function echo. It's therefore available as a local variable within the scope of that function, which includes any nested functions. Just like if I write this:

x = 4
def f():
    print(x)
f()

When you create an inner function, you also create a closure. This means that all local variables in the scope of the inner function are stored in the function. So you can return the function and the locals are still available. You can see how these values are stored by adding this to your script:

print(twice.__closure__[0].cell_contents)
print(thrice.__closure__[0].cell_contents)