Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope of variables in python decorator

I'm having a very weird problem in a Python 3 decorator.

If I do this:

def rounds(nr_of_rounds):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            return nr_of_rounds
        return inner
    return wrapper

it works just fine. However, if I do this:

def rounds(nr_of_rounds):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            lst = []
            while nr_of_rounds > 0:
                lst.append(func(*args, **kwargs))
                nr_of_rounds -= 1
            return max(lst)
        return inner
    return wrapper

I get:

while nr_of_rounds > 0:
UnboundLocalError: local variable 'nr_of_rounds' referenced before assignment

In other words, I can use nr_of_roundsin the inner function if I use it in a return, but I can't do anything else with it. Why is that?

like image 962
Ben Avatar asked Apr 21 '15 00:04

Ben


People also ask

What are the scopes of variables in Python?

What is Variable Scope in Python? In programming languages, variables need to be defined before using them. These variables can only be accessed in the area where they are defined, this is called scope. You can think of this as a block where you can access variables.

What is the difference between closure and decorator in Python?

A decorator is a function that takes in a function and returns an augmented copy of that function. When writing closures and decorators, you must keep the scope of each function in mind. In Python, functions define scope. Closures have access to the scope of the function that returns them; the decorator's scope.

What is the biggest advantage of the decorator in Python?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

What is the use of scope in Python?

A variable is only available from inside the region it is created. This is called scope.


1 Answers

Since nr_of_rounds is picked up by the closure, you can think of it as a "read-only" variable. If you want to write to it (e.g. to decrement it), you need to tell python explicitly -- In this case, the python3.x nonlocal keyword would work.

As a brief explanation, what Cpython does when it encounters a function definition is it looks at the code and decides if all the variables are local or non-local. Local variables (by default) are anything that appear on the left-hand side of an assignment statement, loop variables and the input arguments. Every other name is non-local. This allows some neat optimizations1. To use a non-local variable the same way you would a local, you need to tell python explicitly either via a global or nonlocal statement. When python encounters something that it thinks should be a local, but really isn't, you get an UnboundLocalError.

1The Cpython bytecode generator turns the local names into indices in an array so that local name lookup (the LOAD_FAST bytecode instruction) is as fast as indexing an array plus the normal bytecode overhead.

like image 88
mgilson Avatar answered Oct 16 '22 10:10

mgilson