low mumble. A lined arrow pointing to the left all

Closures are confusing

Closures are confusing, but they're also really not. Here's a basic example.

def create_counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        return count

    return increment

counter = create_counter()

print(counter()) # prints 1
print(counter()) # prints 2
print(count) # NameError: name 'count' is not defined

In the above example, count exists inside the create_counter function and the function "closes over" the data stored in count. This means that count is inaccessible outside of the function, which keeps that data more secure (good). This sort of data encapsulation gives a programmer more control over how data might be manipulated.

An intuition might be to just define count globally, then use an increment function to achieve the same result.

count = 0

def increment():
    global count
    count += 1
    return count

print(increment()) # prints 1
print(increment()) # prints 2
print(count) # prints 2

While this works, having count exposed like this leaves it vulnerable to manipulation in undesirable ways. With a simple count, the consequences are minimal, but when dealing with something like a bank balance, it becomes much more important to encapsulate data and control the mechanisms for manipulating that data.