Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unbound local variable problem in Python

I've got a following code snippet:

def isolation_level(level):
    def decorator(fn):
        def recur(level, *args, **kwargs):
            if connection.inside_block:
                if connection.isolation_level < level:
                    raise IsolationLevelError(connection)
                else:
                    fn(*args, **kwargs)
            else:
                connection.enter_block()
                try:
                    connection.set_isolation_level(level)
                    fn(*args, **kwargs)
                    connection.commit()
                except IsolationLevelError, e:
                    connection.rollback()
                    recur(e.level, *args, **kwargs)
                finally:
                    connection.leave_block()
        def newfn(*args, **kwargs):
            if level is None: # <<<< ERROR MESSAGE HERE, Unbound local variable `level`
                if len(args):
                    if hasattr(args[0], 'isolation_level'):
                        level = args[0].isolation_level
                elif kwargs.has_key('self'):
                    if hasattr(kwargs['self'], 'isolation_level'):
                        level = kwargs.pop('self', 1) 
            if connection.is_dirty():
                connection.commit()
            recur(level, *args, **kwargs)
        return newfn
    return decorator

It really doesn't matter what it does, however I post it in its original form, as I was unable to recreate the situation with anything simpler.

The problem is that when I call isolation_level(1)(some_func)(some, args, here) I get Unbound local variable exception in line 21 (marked on the listing). I don't understand why. I tried recreating the same structure of functions and function calls that wouldn't contain all the implementation details to figure out what is wrong. However I don't get the exception message then. For example the following works:

def outer(x=None):
    def outer2(y):
        def inner(x, *args, **kwargs):
            print x
            print y
            print args
            print kwargs
        def inner2(*args, **kwargs):
            if x is None:
                print "I'm confused"
            inner(x, *args, **kwargs)
        return inner2
    return outer2

outer(1)(2)(3, z=4)

Prints:

1
2
(3,)
{'z': 4}

What am I missing??

EDIT

Ok, so the problem is that in the first version I actually perform an assignment to the variable. Python detects that and therefore assumes the variable is local.

like image 765
julx Avatar asked Apr 29 '11 22:04

julx


People also ask

What is meant by Unbound local error in Python?

The UnboundLocalError: local variable referenced before assignment error is raised when you try to assign a value to a local variable before it has been declared. You can solve this error by ensuring that a local variable is declared before you assign it a value.

What is unbound variable in Python?

A symbol that has not been given a value by assignment or in a function call is said to be “unbound.”

How do I change a local variable to global in Python?

Normally, when you create a variable inside a function, that variable is local, and can only be used inside that function. To create a global variable inside a function, you can use the global keyword.

What is non local in Python?

The nonlocal keyword is used to work with variables inside nested functions, where the variable should not belong to the inner function. Use the keyword nonlocal to declare that the variable is not local.


1 Answers

Local variables are determined at compile time: the assignments to the variable level a few lines below the line the error occurs in make that variable local to the inner function. So the line

if level is None:

actually tries to access a variable level in the innermost scope, but such a variable does not exist yet. In Python 3.x, you can solve this problem by declaring

nonlocal level

at the beginning of the inner function, if you actually want to alter the variable of the outer function. Otherwise, you can simply use a different variable name in the inner function.

like image 59
Sven Marnach Avatar answered Sep 18 '22 06:09

Sven Marnach