A long, long time ago in a lesson, a Python lesson, far, far away
I very briefly mentioned that we can nest functions inside
of functions, and I think that was in the scoping lesson.
And I said, "Hey, we would talk about nested functions".
So let's go ahead and talk about nested functions.
So we know we can have a function like this 'def func', and
it can "Do a thing", and we can run that function.
But inside of Python, we can actually write functions inside
of functions. So we could say function,
let's just rename this one to 'main_func'.
It's going to, "Do a thing".
And in here we have another function called 'second_func',
and this one is just going to print 2-2-2-2-2, because it is the second one.
Let's go ahead rerun that cell, and change this to 'main_func()',
and we're going to see that it simply says print, "Do a thing",
and this is not actually going to run 'second_func' at all.
We need to tell it to run that.
Now, due to scoping, we can't run it automatically from here.
It needs to be run from the inside.
So let's go ahead, take that 'second_func', and run it inside
of the 'main_func'.
So if we run that cell and rerun that cell, we can see both
of them now execute.
Now, at this point in time in your Python learning journey,
this really shouldn't come as a surprise at any point.
We've done a lot of nesting with all sorts of different things,
but the reason we would want to do a nested function like
this, well, there's a few reasons.
One is a decorator, which we'll talk about in the future,
but another reason is maybe in here in the 'main_func',
there are several smaller functions, and we need to perform
something that is fairly complex.
But that 'second_func' doesn't necessarily need to be in
the global scope. It only really needs to be inside of this function,
so it's only applicable to 'main_func'.
So this is one example.
Let's go ahead and create one more example, and here something
that might actually be named a little better than 'main_func'
and 'second_func'.
We have a 'parent()' function, and this could take a 'name',
default name of "Henry".
We could say "parent() was run with ", and that's an
f-string so that we can get "Henry" right in there.
Now, inside of here, let's say we have a 'child()' function.
'child' function, and the 'name' in here is going to be "Zephyr",
and we want to say 'print()', f-string, 'f"
child was run with "'.
So even if we run 'parent', the 'child' function is not actually
going to run unless we tell it to run.
So then we need to run 'child' in here and outside
we can simply run 'parent'.
And it says, "parent was run with Henry", and "child was run with Zephyr".
If we comment this out, so effectively it doesn't exist to Python
anymore. We only get the one 'print' statement.
Now, what's interesting about this is we can actually return
entire functions.
We don't need to return just a value in here or a 'print' statement.
So let's say 'child' is actually going to return a number
of 159. I don't know what that number means, if there's any
significance behind it.
I just guessed 159.
And what this is going to do is 'return child', not 'return
child()' with parentheses.
We're going to return the entire 'child' function.
So let's go ahead and run that.
And let's throw this into a variable. Let's just call it 'var'.
When we're going to run 'parent', it's going to automatically run the
'print' statement in here.
'parent' was run with default 'name' of "Henry".
Then we defined a 'child' with a default 'name' of "Zephyr".
That's actually not useful anymore.
We got rid of that.
But the 'child()' function is going to 'return 159',
and then we're going to return the entire 'child' function
itself, not the value of 159, the entire function itself.
So let's run that. Run that.
And what happens when we do 'var'?
[no audio]
We can see that 'var' is a
function. We can see that it was being called from a function
called 'parent'. It has 'locals' in here,
so variables, and it's being called from a function called
'child', a sub-function if you want to call that.
What we can do, is because this is now actually a function,
it's a variable, but it's also technically a function,
we can now just call it as a function, and 'child' is now returning
159 as 'var'.
I would say this was a fairly confusing example because we
use 'parent' and 'child', which I guess sort of makes sense,
but we can actually make a little more relatable chunk of
code as well. So let's create a new function in here, and let's call it
'dog()', and we're going to create a child function in here called
'speak()', and all this is going to do is 'return
"Woof. Woof",
and in here, the 'dog()' function is going to return the entire
'speak' function.
And essentially all this is doing is 'dog' is now a wrapper for 'speak'.
And now we could say 'dog = dog()'.
However, that's going to 'return speak'.
So if we read through this, we're going to run this,
it will register 'speak', and then it will actually return
the entire function itself.
So let's say this is 'woof = dog()'.
'woof' by itself is a function.
And because we know it's a function, and it says that right
there, we can use parentheses around it.
And 'woff' now says, "Woof woof!"
So essentially, we've really just taken a function, a nested
function, and renamed it to any variable that we want.
Now, you might be thinking to yourself, "Kalob, why?
Why is this important?
This doesn't seem like I would ever use something like this".
And you may, you may not.
I use nested functions quite often, because I like to, let's
say, in the 'dog()' function here, I like to wrap up a bunch of logic.
So if I have, let's say, 50 lines of code inside of here,
and it's all just like this one time logic, I don't necessarily
want to write 50 lines of code.
What I want to do is put it in a function,
and then I can say, I don't know, the 'total =', whatever
'speak' would be, the 50 lines of code inside of 'speak()',
and then I could do something with that.
And then in my code editor, and we actually have this from
the last example, I can actually just close up my code, and
just makes it a little easier to maintain.
That way, you can read the code a lot easier if you don't have
to actually look at all the code all the time, especially
if not all the code is totally applicable all the time.
The second reason is for decorators, and we're going to be
learning about decorators pretty soon.
And the idea behind a decorator is basically, instead of
taking a regular variable as a function parameter, it takes
a function as a parameter,
and then we work with that.
So while this may not be super applicable to you right now,
it is good to tuck in your back pocket, because in the next
lesson, we're definitely going to be using this.