DEV Community

Alex Miasoiedov
Alex Miasoiedov

Posted on

4 2

Python functional closure scopes without global or nonlocal

A typical decorator in python or any nested closured functions is usually referring to the state from one or more variable scope above.


def wrapper():

    counter = 0

    def incr():
        counter += 1
        return counter
    return incr

fn = wrapper()
print(fn())


# Traceback (most recent call last):
#   File "sample.py", line 51, in <module>
#     print(fn())
#   File "sample.py", line 46, in incr
#     return counter
# NameError: name 'counter' is not defined


Enter fullscreen mode Exit fullscreen mode

Like in this example with counter variable. The code will fail to execute because counter is not visible within incr function scope.

The typical fix is usually either to declare counter as nonlocal or even global .



def wrapper():

    counter = 0

    def incr():
        nonlocal counter
        counter += 1
        return counter
    return incr


fn = wrapper()
print(fn())

# >> 1

Enter fullscreen mode Exit fullscreen mode

This works as expected but is the other way achieve the same?



def wrapper():
    v = dict(counter=0)

    def incr():
        v['counter'] += 1
        return v['counter']
    return incr


fn = wrapper()
print(fn())

# > 1

Enter fullscreen mode Exit fullscreen mode

As the first trick we can use mutable object like dict to declare and modify the state without nonlocal keyword. Why does it work? Comparing to previous example counter += 1 this is actually syntactic sugar for counter = counter + 1 and python interpret usually has no clue how to resolve to which scope reassign this new counter variable. However if it just mutation by object reference there is no such ambiguity

Another trick derives from the previous example but I found it more readable and concise.


def wrapper():
    class Vars:
        counter = 0

    def incr():
        Vars.counter += 1
        return Vars.counter
    return incr


fn = wrapper()
print(fn())
# >> 1

Enter fullscreen mode Exit fullscreen mode

Original post

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more