avatarOliver S

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3598

Abstract

make too much sense yet, so let’s introduce a slightly better one (and refer the reader to the examples at the end of this article):</p><div id="c6d7"><pre><span class="hljs-keyword">def</span> <span class="hljs-title function_">greeter</span>(<span class="hljs-params">name</span>): <span class="hljs-keyword">def</span> <span class="hljs-title function_">greet</span>(): <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Welcome <span class="hljs-subst">{name}</span>!"</span>) <span class="hljs-keyword">return</span> greet

peter_greeter = greeter(<span class="hljs-string">"Peter"</span>) hans_greeter = greeter(<span class="hljs-string">"Hans"</span>)

peter_greeter() hans_greeter()</pre></div><p id="3f37">Above, we define a greeter function, which can be used to generate personal greeting calls.</p><h1 id="0e1a">Modifying Variables Inside Closures</h1><p id="6508">We now extend our above example to also count how often a specific person is greeted, for this introducing a counter variable. However, a naive version will not run — we have to use the <code>nonlocal</code> keyword to allow assigning values to variables from outer scopes:</p><div id="2500"><pre><span class="hljs-keyword">def</span> <span class="hljs-title function_">greeter</span>(<span class="hljs-params">name</span>): count = <span class="hljs-number">0</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">greet</span>(): <span class="hljs-keyword">nonlocal</span> count count += <span class="hljs-number">1</span> <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Welcome <span class="hljs-subst">{name}</span>! I have greeted them <span class="hljs-subst">{count}</span> many times so far."</span>) <span class="hljs-keyword">return</span> greet

peter_greeter = greeter(<span class="hljs-string">"Peter"</span>) hans_greeter = greeter(<span class="hljs-string">"Hans"</span>)

peter_greeter() hans_greeter() peter_greeter() peter_greeter()</pre></div><p id="4148">The reason for this is that <code>count</code> is an int, and thus an immutable type. For mutable types, such as Lists or Dictionaries, <code>nonlocal</code> is not required:</p><div id="2fe4"><pre><span class="hljs-keyword">def</span> <span class="hljs-title function_">greeter</span>(<span class="hljs-params">name</span>): summary_dict = {<span class="hljs-string">"name"</span>: name, <span class="hljs-string">"count"</span>: <span class="hljs-number">0</span>} <span class="hljs-keyword">def</span> <span class="hljs-title function_">greet</span>(): summary_dict[<span class="hljs-string">"count"</span>] += <span class="hljs-number">1</span> <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Welcome <span class="hljs-subst">{summary_dict[<span class="hljs-string">'name'</span>]}</span>! I have greeted them <span class="hljs-subst">{summary_dict[<span class="hljs-string">'count'</span>]}</span> many times so far."</span>) <span class="hljs-keyword">return</span> greet

peter_greeter = greeter(<span class="hljs-string">"Peter"</span>) hans_greeter = greeter(<span class="hljs-string">"Hans"</span>)

peter_greeter() hans_greeter() peter_greeter() peter_greeter()</pre></div><h1 id="26f6">Why Use Closures</h1><p id="c07e">Overall, they support the idea of data hiding, and reduce the need for global variables.</p><p id="2484">Even though related, in my opinion their main use-case is the replacement of classes with just a single function besides <code>init()</code

Options

. Consider the simple greeter counter from above. Using a class, this would look as follows:</p><div id="c330"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">GreeterClass</span>: <span class="hljs-keyword">def</span> <span class="hljs-title function_">init</span>(<span class="hljs-params">self, name</span>): self.greet_name = name self.count = <span class="hljs-number">0</span>

<span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self</span>):
    self.count += <span class="hljs-number">1</span>
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Welcome <span class="hljs-subst">{self.greet_name}</span>! I have greeted them <span class="hljs-subst">{self.count}</span> many times so far."</span>)

peter_greeter = GreeterClass(<span class="hljs-string">"Peter"</span>) hans_greeter = GreeterClass(<span class="hljs-string">"Hans"</span>)

peter_greeter() hans_greeter() peter_greeter() peter_greeter()</pre></div><p id="b690">It can be argued, that a closure is more concise and to the point.</p><h1 id="448a">Examples</h1><p id="14bb">Let’s finish with some more realistic examples. In the first, we want to count the number of times a function is called by wrapping it inside a closure. Note this is closely related to <a href="https://readmedium.com/816236066ed8">Python decorators</a>, and, depending on the use-case, these probably are preferred here.</p><div id="7303"><pre><span class="hljs-keyword">def</span> <span class="hljs-title function_">call_counter</span>(<span class="hljs-params">func</span>): call_count = <span class="hljs-number">0</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">call</span>(<span class="hljs-params">*args, **kwargs</span>): <span class="hljs-keyword">nonlocal</span> call_count call_count += <span class="hljs-number">1</span> <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Calling <span class="hljs-subst">{func.name}</span>. This is call number: <span class="hljs-subst">{call_count}</span>."</span>) func(*args, **kwargs) <span class="hljs-keyword">return</span> call

<span class="hljs-keyword">def</span> <span class="hljs-title function_">printer</span>(<span class="hljs-params">msg</span>): <span class="hljs-built_in">print</span>(msg)

call_printer = call_counter(printer) call_printer(<span class="hljs-string">"call 1"</span>) call_printer(<span class="hljs-string">"call 2"</span>)</pre></div><p id="6c2a">In the next example we define a short logger:</p><div id="be62"><pre><span class="hljs-keyword">def</span> <span class="hljs-title function_">create_logger</span>(): log_content = <span class="hljs-string">"Start of the log.\n"</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">log</span>(<span class="hljs-params">msg</span>): <span class="hljs-keyword">nonlocal</span> log_content log_content += msg + <span class="hljs-string">"\n"</span> <span class="hljs-keyword">return</span> log_content <span class="hljs-keyword">return</span> log

logger = create_logger() log_content = logger(<span class="hljs-string">"Log A"</span>) log_content = logger(<span class="hljs-string">"Log B"</span>) <span class="hljs-built_in">print</span>(log_content)</pre></div><p id="c9ac">Please let me know if you have any questions or comments, and feel free to subscribe if you liked this post!</p></article></body>

Closures in Python

About Data Hiding and Small Class Alternatives

In Python, a closure is a nested function, which accesses variables from its enclosing scope, and is returned by another function. This can be useful to avoid small classes containing only a single function, promotes data hiding and offers alternatives to global variables.

In this article we’ll first introduce and describe closures, and then show their application on different examples.

Photo by Rubaitul Azad on Unsplash

Nested Functions

Before coming to closures, let’s talk about nested functions: in Python, functions can be defined nested, i.e. within each other:

def foo(x):
    def bar():
        print(f"bar: {x}")
    print(f"foo: {x}")
    bar()

foo(0)

In this examples, foo is the outer function, and inside it, we have defined another function named bar — a nested, or inner function. bar can be called from inside foo, but not from outside, as it is not valid in this scope. Further, we can see that bar has access to foo’s variables, such as x. This is known as a free variable — a variable which is used in a code block, but not defined there.

Closures

With this, we can define closures. Since functions are first-class objects in Python, we can also return them without calling them. Initiating this small change yields a closure from above example:

def foo(x):
    def bar():
        print(f"bar: {x}")
    print(f"foo: {x}")
    return bar

my_closure = foo(0)
my_closure()

Now, foo returns the function bar, and via my_closure = foo(0) we define a closure — and obtain an object, in which the variable x (in this case 0) is “bound” to the returned object — even though x is not available anymore later. Let us re-visit the definition of a closure: a closer is an inner function, which access variables from its enclosing scope (free variables), and is returned by another function. This is given here.

Of course, this example does not make too much sense yet, so let’s introduce a slightly better one (and refer the reader to the examples at the end of this article):

def greeter(name):
    def greet():
        print(f"Welcome {name}!")
    return greet

peter_greeter = greeter("Peter")
hans_greeter = greeter("Hans")

peter_greeter()
hans_greeter()

Above, we define a greeter function, which can be used to generate personal greeting calls.

Modifying Variables Inside Closures

We now extend our above example to also count how often a specific person is greeted, for this introducing a counter variable. However, a naive version will not run — we have to use the nonlocal keyword to allow assigning values to variables from outer scopes:

def greeter(name):
    count = 0
    def greet():
        nonlocal count
        count += 1
        print(f"Welcome {name}! I have greeted them {count} many times so far.")
    return greet

peter_greeter = greeter("Peter")
hans_greeter = greeter("Hans")

peter_greeter()
hans_greeter()
peter_greeter()
peter_greeter()

The reason for this is that count is an int, and thus an immutable type. For mutable types, such as Lists or Dictionaries, nonlocal is not required:

def greeter(name):
    summary_dict = {"name": name, "count": 0}
    def greet():
        summary_dict["count"] += 1
        print(f"Welcome {summary_dict['name']}! I have greeted them {summary_dict['count']} many times so far.")
    return greet

peter_greeter = greeter("Peter")
hans_greeter = greeter("Hans")

peter_greeter()
hans_greeter()
peter_greeter()
peter_greeter()

Why Use Closures

Overall, they support the idea of data hiding, and reduce the need for global variables.

Even though related, in my opinion their main use-case is the replacement of classes with just a single function besides __init__(). Consider the simple greeter counter from above. Using a class, this would look as follows:

class GreeterClass:
    def __init__(self, name):
        self.greet_name = name
        self.count = 0

    def __call__(self):
        self.count += 1
        print(f"Welcome {self.greet_name}! I have greeted them {self.count} many times so far.")

peter_greeter = GreeterClass("Peter")
hans_greeter = GreeterClass("Hans")

peter_greeter()
hans_greeter()
peter_greeter()
peter_greeter()

It can be argued, that a closure is more concise and to the point.

Examples

Let’s finish with some more realistic examples. In the first, we want to count the number of times a function is called by wrapping it inside a closure. Note this is closely related to Python decorators, and, depending on the use-case, these probably are preferred here.

def call_counter(func):
    call_count = 0
    def call(*args, **kwargs):
        nonlocal call_count
        call_count += 1
        print(f"Calling {func.__name__}. This is call number: {call_count}.")
        func(*args, **kwargs)
    return call

def printer(msg):
    print(msg)
    
    
call_printer = call_counter(printer)
call_printer("call 1")
call_printer("call 2")

In the next example we define a short logger:

def create_logger():
    log_content = "Start of the log.\n"
    def log(msg):
        nonlocal log_content
        log_content += msg + "\n"
        return log_content
    return log

logger = create_logger()
log_content = logger("Log A")
log_content = logger("Log B")
print(log_content)

Please let me know if you have any questions or comments, and feel free to subscribe if you liked this post!

Python
Python3
Recommended from ReadMedium