Python UnboundLocalError resolve the root causes of errors and NameError

 This article is to understand the concepts pave the way for the closure, detailed in-depth follow-up will sort out closure-related blog, so stay tuned.

1. Case Study

In the process of finishing closures related concepts, it is often found UnboundLocalError and NameError two errors encountered in the beginning can be very confusing, such errors can not start.

1.1 Case I:

def outer_func():
    loc_var = "local variable"
    def inner_func():
        loc_var += " in inner func"
        return loc_var
    return inner_func

clo_func = outer_func()
clo_func()

Error message:

Traceback (most recent call last):
  File "G:\Project Files\Python Test\Main.py", line 238, in <module>
    clo_func()
  File "G:\Project Files\Python Test\Main.py", line 233, in inner_func
    loc_var += " in inner func"
UnboundLocalError: local variable 'loc_var' referenced before assignment

1.2 Case II:

1 def get_select_desc(name, flag, is_format = True):
2     if flag:
3         sel_res = 'Do select name = %s' % name
4     return sel_res if is_format else name
5 
6 get_select_desc('Error', False, True)

Error message:

Traceback (most recent call last):
  File "G:\Project Files\Python Test\Main.py", line 247, in <module>
    get_select_desc('Error', False, True)
  File "G:\Project Files\Python Test\Main.py", line 245, in get_select_desc
    return sel_res if is_format else name
UnboundLocalError: local variable 'sel_res' referenced before assignment

1.3 Case 3:

def outer_func(out_flag):
    if out_flag:
        loc_var1 = 'local variable with flag'
    else:
        loc_var2 = 'local variable without flag'
    def inner_func(in_flag):
        return loc_var1 if in_flag else loc_var2
    return inner_func

clo_func = outer_func(True)
print clo_func(False)

Error message:

Traceback (most recent call last):
  File "G:\Project Files\Python Test\Main.py", line 260, in <module>
    print clo_func(False)
  File "G:\Project Files\Python Test\Main.py", line 256, in inner_func
    return loc_var1 if in_flag else loc_var2
NameError: free variable 'loc_var2' referenced before assignment in enclosing scope

 The above three examples may seem a bit artificial, but in fact are more or less similar to the error code can be found in the shadow in the example above. Here are just to illustrate concepts, for example rationality itself does not have to do too much attention.

2. The cause of the error

Since python is not declared the concept of variable, function, or class. Written in C or C ++ python in accordance with the habit, may be difficult to discover the source of the error is.

First look at the official interpretation of such errors:

When a name is not found at all, a NameError exception is raised. If the name refers to a local variable that has not been bound, a UnboundLocalError exception is raised. UnboundLocalError is a subclass of NameError.

Probably it means:

If you reference a variable, but the variable name is not found, the type of error is NameError. If the name is not yet bound a local variable names, then the type of error is NameErrorUnboundLocalError错误

The following NameError this type of error may better understand some of them:

1 my_function()
2 def my_function():
3     pass

 If the python interpreter to execute () when bound to my_function to def my_function, but this time also represents the my_function entry memory function to execute. Therefore, before using my_function will have to be NameError error.

Then the first example above, the use of variables, has the assignment operator (can be regarded as a binding operation, the back will say), why is it wrong when referring to? Visibility determination may also be defined

If we say that is because the assignment operation is not performed, then why the variable name in the local namespace is visible? (Not visible, then this error will be: NameError: global name 'xxx' is not defined, according to UnboundLocalErrorthe definition of the visibility can be determined)

Where is the problem in the end? How to correctly understand the above three examples of errors?

3. Visibility and bind 

For simplicity, this does not describe the namespace variable lookup rules LGB related concepts.

In C or C ++, as long as the declaration and definition of a variable or function, it can be used directly. But in Python To quote a name, the name must be visible and be binding.

First look at a few concepts:

  1. code block: some python program text is executed as a unit (Unit). For example a module, the definition of other body functions and classes.
  2. scope: define the visibility in the name of a code block;
  3. apos block Environment : For a code block, the block constituting a set of all of its scope visible name environment.
  4. bind name: The following operations can be regarded as binding operation
    • Shape function parameters
    • import declaration
    • Class and method definitions of
    • Assignment
    • The for loop header
    • Abnormal capture the relevant variable assignment
  5. local variable: If the name is bound in a block, the variable is a local variable of the block.
  6. global variable: If the name is bound in a module, this variable will be referred to as a global variable.
  7. free variable: If a name is referenced in a block but not defined in the code block, then it is called the variable is a free variable.

Free variable is an important concept in the parent function closure referenced in the local variable is a free variable, and the free variable is stored in a cell object. The closures will be introduced in the relevant article. 

scope scalable in function, but does not scalable in the class definition.

Analysis tidy:

After some of the concepts above description we know, as long as a variable in their code block has a bind operation, then the scope code block will be included in this variable.

That is the decision of the binding operation, and the bound name is visible in the current scope (if it is a function, it also included the definition of scope), the operation before actually binding even in the name.

Here there will be a problem, that is, if you reference the name before the name binding operation, it will be a problem, even if the name is visible.

If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.

Note that the official description above and the first sentence of the last sentence.

In general is a code block, all in the name binding operation is bound can be regarded as a local variable; it was not until the binding operation can really refer to the name after being executed.

With these concepts, one by one look at the following analysis of the above three cases.

4. Error parsing

4.1 Analysis of a Case

In outer_func loc_var we define the variable, because the assignment is a binding operation, thus having loc_var visibility, and is bound to a particular string object.

However, the function of which is defined in inner_func did not reference the function in scope can not be extended to all defined within the scope of it?

The following two paragraphs describe what the official point of view in the:

When a name is used in a code block, it is resolved using the nearest enclosing scope.

This passage tells us that when a name is referenced, he will find definitions referenced in its recent name in scope. Obviously loc_var + = "in inner func" This statement will first find the name loc_var loc_var inside the function inner_func in.

The statement is in fact equivalent to loc_var = loc_var + "in inner func", loc_var variables to the right of the equal sign will first be used, but will not use here outer_func defined loc_var, because there loc_var in the scope of the function inner_func assignment, so the scope of variable inner_func is visible as a local variable of inner_func.

But to wait for the completion of the implementation of the statement, can really bind loc_var. This statement is, we use a local variable in the previous inner_func block is bound. The error type defined above, which is aUnboundLocalError.

4.2 Case II analysis

In this case, it looks like there is a problem, but do not know how to explain.

Reference occurs after the bind operation, the variable should be referenced properly. But the problem is that the assignment (binding operation) may not be executed. If you do not bind operation so there will certainly be a reference of variables, this has already been explained before.

But there is a question may be that, if the assignment statement is not executed, the variables in the current block is visible Why?

On this issue may in fact be above paragraph explanation: The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.

As long as the binding operation (regardless of the actual have not been executed), then bound name as a local variable, that is, the current block is visible. scanning text occurs before the code is executed.

4.2 Case III analysis

This example illustrates the main problem for a class of free variable references. At the same time This example also shows the use of a free variable.

When creating closure inner_func, loc_var1 function as the parent and loc_var2 outer_func two local variable is visible in the scope of its internal inner_func. In the closure is referenced returned after closure of the local variable outer_func referred to as a free variable.

free variable in the closure Can be referenced depending on whether they have not been bound to a specific object.

5. Extended Case

Next, look at an example:

import sys

def add_path(new_path):
    path_list = sys.path

    if new_path not in path_list:
        import sys
        sys.path.append(new_path)
add_path('./')

Usually inadvertently might commit this mistake above, this is a typical UnboundLocalErrormistake. If you carefully read and understand the above analysis, I believe that should be able to understand the cause of the error. If it is not cleared, then read it again :-)

Guess you like

Origin www.linuxidc.com/Linux/2020-02/162395.htm