— code, python, 🐍 — 1 min read
1def factorial(N):2 if N < 0:3 raise ValueError("Sorry. We don't do that here...")4 def compute_factorial(N, accumulated=1):5 if N<=1:6 return accumulated7 return compute_factorial(N-1, N*accumulated)8 return compute_factorial(N)9''' 10>>> factorial(10)1136288001213>>> compute_factorial14Traceback (most recent call last):15 File "<stdin>", line 1, in <module>16NameError: name 'compute_factorial' is not defined17'''
Here we see the inner function compute_factorial
being used from the outer-scope of the
method factorial
. Nothing fancy here, just a bland example of what inner function is. In the above case we can move compute_factorial
outside to the same level as factorial
and
out program would run as expected...
1def compute_factorial(N, accumulated=1):2 if N<=1:3 return accumulated4 return compute_factorial(N-1, N*accumulated)5def factorial(N):6 if N < 0:7 raise ValueError("Sorry. We don't do that here...")8 return compute_factorial(N)9'''10>>> factorial(10)11362880012>>> compute_factorial13<function compute_factorial at 0x10188daf0>14'''
Although now, compute_factorial
is accesible to all other callers who can call factorial
. There lies one benifit of inner function: Encapsulation
Dialing things up a notch is the property of inner functions which have access to outer functions scope and thus its variables.
1def echo(text, to_upper=False):2 def inner_echo():3 return "Hah take this: " + ( text.upper() if to_upper else text)4 return inner_echo()5'''6>>> echo("hello", True)7'Hah take this: HELLO'8'''
As you can see from the output, inner function have access to variables defined in the scope of the outer function.
Try and guess the execution of the following methods: ( 😉 to a future blog post )
1def test_dict():2 memory = {}3 def inner_test():4 for i in range(10):5 memory[i] = i*i6 inner_test()7 return memory89def test_int():10 number = 10011 def inner_test():12 for i in range(number):13 number -= i14 inner_test()15 return number
In python functions are first class objects. Few properties of first class functions:
Closure is a function object that remembers values in enclosing scopes even if they are not present in memory any longer. Let's look at an example to understand better:
1def exponent(number):2 def inner_exponent(value):3 return number ** value4 return inner_exponent5'''6>>> two_exponent = exponent(2)7>>> two_exponent(10)810249>>> two_exponent10<function exponent.<locals>.inner_exponent at 0x1018a61f0>11'''
In the above example exponent
is being used as a factory method which takes number
as an argument and returns a new method inner_exponent
which is a function object i.e. which uses the data passed in the outer function for its own execution much later after the outer function has been executed.
To put it another way, the closure "initializes" the number
in inner_exponent()
and then returns it. Now, whenever you call that newly returned function, it will always see its own private snapshot that includes number
.
Closures can be really powerful when you want to encapsulate information/logic from the caller via inner functions.