UnboundLocalError,探讨Python中的绑定

绑定

将python闭包之前,先梳理一下闭包中的绑定操作。

先看看2个相关的错误 NameErrorUnboundLocalError

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.

NameError比较好理解,即引用未定义,例如

fun1()
def fun1():
    pass

但是UnboundLocalError却比较隐晦,意思是引用变量未绑定,注意这里的变量可能是已经定义了的。

**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.

下面来看看关于这种错误的隐晦bug。

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()

#UnboundLocalError: local variable 'loc_var' referenced before assignment

程序在执行clo_func()的时候出了问题。
注意语句loc_var += " in inner func"可以看作loc_var = loc_var + " in inner func", 而在求取算式loc_var + " in inner func"的时候需要变量loc_var的值,而变量loc_var在函数inner_func内是有定义的,即loc_var += " in inner func", 因此inner_func会去引用该值,但是inner_func的loc_var却没有完成绑定,因此出现了UnboundLocalError错误,有点类似递归死循环。

由此可见,python中总是优先引用自身代码块内出现的变量,不管先后次序
注意这里是不管先后次序,因此可能引发UnboundLocalError

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.

再举一个类似的例子

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('./')

此处path_list = sys.path引用第2个 import sys 而不是第一个, 由此导致了引用未绑定,因为sys被当成了还没有import。

bind name:下面的操作均可视为绑定操作

  • 函数的形参
  • import声明
  • 类和函数的定义
  • 赋值操作
  • for循环首标
  • 异常捕获中相关的赋值变量

还有一些关于UnboundLocalError的其他例子,如下

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)

这种错误在编译的时候不会出错,但是在运行某些例子的时候就会出现UnboundLocalError。

那如何避免这种错误呢?我觉得可以注意以下几点

  • 当局部变量与全局变量重名时

  • 当通过函数参数来选择是否绑定变量时

这种错误和诸如C, C++等其他语言有较大差别,我觉得可能是因为在这些语言中变量的定义是明确的,如int i = 1; 。而在python中却不需要明确定义变量,因此python的每一条赋值语句在某种程度上可以说都是一次定义。但有时候这样子很不方便,于是python里也有了相应的语法,nonlocal与global定义, 但是注意nonlocal是python3的语法

参考 https://www.cnblogs.com/yssjun/p/9873689.html

原文地址:https://www.cnblogs.com/friedCoder/p/12571983.html