Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python closure with assigning outer variable inside inner function

I've got this piece of code:

#!/usr/bin/env python

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache=[v]
    return cache
  return match
m = get_match()
m(1)

if I run it, it says:

UnboundLocalError: local variable 'cache' referenced before assignment

but if I do this:

#!/usr/bin/env python

def get():
  y = 1
  def m(v):
    return y + v
  return m

a=get()
a(1)

it runs.

Is there something with list? or my code organizing is wrong?

like image 315
nemo Avatar asked Aug 23 '12 12:08

nemo


People also ask

Can Python inner function access outer variable?

Python Inner Functions or Nested Functions can access the variables of the outer function as well as the global variables. The inner functions variable has a local scope that is limited only to that function. Inner Functions variables can't be accessed at the outer function scope.

How do you access a variable inside a function in Python?

The variables that are defined inside the methods can be accessed within that method only by simply using the variable name. Example – var_name. If you want to use that variable outside the method or class, you have to declared that variable as a global.

How do you use variables in nested functions?

Nested functions can access variables of the enclosing scope. In Python, these non-local variables are read-only by default and we must declare them explicitly as non-local (using nonlocal keyword) in order to modify them. Following is an example of a nested function accessing a non-local variable.

Is closure a nested function?

Closure are nested function which has access to the outer scope. After the outer function is returned, by keeping a reference to the inner function (the closures) we prevent the outer scope to be destroyed.


1 Answers

The problem is that the variable cache is not in the scope of the function match. This is not a problem if you only want to read it as in your second example, but if you're assigning to it, python interprets it as a local variable. If you're using python 3 you can use the nonlocal keyword to solve this problem - for python 2 there's no simple workaround, unfortunately.

def f():
    v = 0

    def x():
        return v    #works because v is read from the outer scope

    def y():
        if v == 0:  #fails because the variable v is assigned to below
            v = 1

    #for python3:
    def z():
        nonlocal v  #tell python to search for v in the surrounding scope(s)
        if v == 0:
            v = 1   #works because you declared the variable as nonlocal

The problem is somewhat the same with global variables - you need to use global every time you assign to a global variable, but not for reading it.

A short explanation of the reasons behind that: The python interpreter compiles all functions into a special object of type function. During this compilation, it checks for all local variables the function creates (for garbage collection etc). These variable names are saved within the function object. As it is perfectly legal to "shadow" an outer scopes variable (create a variable with the same name), any variable that is assigned to and that is not explicitly declared as global (or nonlocal in python3) is assumed to be a local variable.

When the function is executed, the interpreter has to look up every variable reference it encounters. If the variable was found to be local during compilation, it is searched in the functions f_locals dictionary. If it has not been assigned to yet, this raises the exception you encountered. If the variable is not assigned to in the functions scope and thus is not part of its locals, it is looked up in the surrounding scopes - if it is not found there, this raises a similar exception.

like image 106
l4mpi Avatar answered Sep 18 '22 19:09

l4mpi