Search code examples
pythonscopefree-variable

How to define a free variable


I am currently reading the book Fluent Python - Luciano Ramalho (a great book IMO).

In the chapter about decorators and closures, there is a following piece of code:

def make_averager():
    series = []

    def averager(value):
        series.append(value)
        total = sum(series)
        return total/len(series)
    return averager


avg = make_averager()

So in this case, series is a free variable, and I can verify this by printing

>>> avg.__code__.co_varnames
('new_value', 'total')
>>> avg.__code__.co_freevars
('series',)

But when I tried refactoring the make_averager():

def make_averager():
    series = []
    total = 0
    count = 0

    def averager(value):
        total += value
        count += 1
        return total/count
    return averager

avg = make_averager()

either series, total or sum is considered a free_variable. Instead, total and sum are now local variables (?) since avg.__code__.co_varnames returns ('value', 'total', 'count') and avg.__code__.co_freevars returns ().

So now series is now no longer a free-variable in the scope of averager, unless I add

series.append(value)

inside averager(value). This is understandable since I don't call series at all inside averager(value), but I couldn't understand what's happening with those 2 variables.

Why are total and count not considered as free-variables, and how do I assign them as free-variables?


Solution

  • nonlocal total, count

    def averager(value):
        nonlocal total
        nonlocal count
        total += value
        count += 1
        return total/count
    

    Why does nonlocal make them free variables?

    Without nonlocal, total += value assigns to total which by default creates a local variable. Since total is in the local scope of the function averager, it is no longer a free variable.