I am viewing python epiphanies by O'Reilly media and they gave this as an example
warning2 = create_logger("warning")
warning2("this is a warning")
WARNING:this is a warning
I knew bit about decorators and the format of
create_logger is exactly like a decorator except it's not being used as one.
what boggles my mind is
warning2("this is a warning")
warning2 contains the
create_logger function with an argument of
so it's basically getting called already
warning2("this is a warning") is like calling it twice?
I did however notice that doing:
create_logger("warning")("this is a warning")
yields the same results
which would lead me to believe that the second call i.e
("this is a warning")
is implicitly referencing the
logger function inside
create_logger and passing the arguments to it?
am I understanding it right?
and when would we ever need to use this?
I'm going to borrow some words from Simeon Franklin on closures (and, by extension, decorators):
Python supports a feature called function closures which means that inner functions defined in non-global scope remember what their enclosing namespaces looked like at definition time.
So yes, this is a closure. Decorators make use of closures, but you don't have to have a decorator to use a closure.
To that, let's take a deeper look at the second part of what was said: the inner function defined in non-global space remembers what its inclosing namespace was at definition time.
At definition time, here's what it would have looked like.
def logger(message): log("warning",message) return logger
Since we pass
"warning" in to
severity at the outer level, its value is bound to "warning" inside of the closure.
At this point, all we've done is define a method that takes a parameter of
message, and we are referencing it through
We then invoke this method via
warning2("this is a warning").
This is also why your currying operation works:
create_logger("warning")("this is a warning") does the exact same thing as the above code, but we're simply not keeping track of the creation of the function with
"warning" bound as its severity anywhere.
Why is this useful? This gives you the ability to write functions that create functions, or give you the ability to deduplicate a lot of code. Imagine if you had to make use of a logger that required you to supply its severity level and a message, when ideally, all you'd need to supply is its message, and you would pick the logger function most suited for the task. This particular snippet of code would enable you to do that.
create_logger is a function whose return value is another function. When you call
create_logger, the function
logger defined inside it is not called; it is just returned. So when you do
warning2 = create_logger('warning'),
warning2 is a function.
warning2("this is a warning") then calls it as normal. The second call does not "implicitly" reference the inner function; the first call returns an explicit reference to the inner function.
As for why you would want to use this, it can be quite powerful. The ability to define functions inside other functions allows you to create a sort of "skeleton" inner function that can provide an outline of some task, and then you can later flesh it out with arguments passed to the outer function. In this case
logger is the "skeleton" whose "flesh" is provided by the call to
create_logger. Decorators, which you mentioned, are one of the most prominent uses of nested functions.