这个问题不是关于解决问题,而是关于理解Python(2.7)编译器部分的内部工作原理。
请参阅下面的代码。它是一个标准的装饰器函数,参数去除了所有花哨的东西。
def deco(basedir):
def wrap(f):
def newf(*args, **kwargs):
if basedir == "": # local or nested?
basedir="empty" # local scope
print basedir # local or nested?
return f(*args, **kwargs)
return newf
return wrap
@deco(basedir="full")
def test1(a):
print a
if __name__ == "__main__":
test1("test1")
当你测试它时,它会失败,UnboundLocalError:局部变量'basedir'在赋值之前引用了if basedir == ": 行。请注意,在此示例中,条件(赋值)的主体从未执行。
但是当你用下面的代码片段替换if时,它突然起作用了。
if basedir == "":
base = "empty"
else:
base = basedir
print base
我希望失败的情况在解释器遇到赋值时创建一个局部变量 basedir,但在 if 语句中使用父级的作用域。但在我看来,当代码中任何地方都存在 basedir= 赋值时,它也尝试在 if 中使用局部变量。
那么任何人都可以向我解释一下在这种情况下 Python 编译器如何创建名称吗?本地范围是否在编译期间提前准备好?当编译器看到对局部变量的赋值时,它是否使用未定义的值预先初始化名称?
您知道这种行为是否在任何地方都有记录吗?
我知道以前在这里解决过类似的情况,但我不是在解决之后。我希望看到解释,理想情况下是指向文档的指针。
相关:"赋值前引用的局部变量" — 只有函数?
问题是Python编译器无法确定basedir
的正确范围。Python 中的范围是静态确定的。但是basedir="empty"
赋值语句使basedir
变量成为newf
函数的局部变量。因此,您无法在第一次赋值之前访问局部变量。
至于您的"修复",您只是删除了basedir="empty"
的分配,basedir
对于newf
来说不是更本地的,但它从deco
功能中使用。
在 Python 2 中,你只能全局或局部设置变量,不能嵌套:
global_variable_that_is_global = 1
global_variable_that_is_only_used_locally = 2
def function():
nested_variable_that_can_not_be_set_by_inner_functions = 3
def inner_function():
global global_variable_that_is_global
global_variable_that_is_global = "changed"
global_variable_that_is_only_used_locally = "changed"
nested_variable_that_can_not_be_set_by_inner_functions = "changed"
def get_nested_variable():
return nested_variable_that_can_not_be_set_by_inner_functions
return inner_function, get_nested_variable
当你执行这个时,你可以试试这个:
>>> inner_function, get_nested_variable = function()
>>> get_nested_variable()
3
>>> inner_function()
>>> get_nested_variable()
3
>>> global_variable_that_is_only_used_locally
2
>>> global_variable_that_is_global
'changed'
因此,与Python 2相比,Python 3中有一个非本地关键字:
>>> def function():
nonlocal_variable = 1
def inner_function():
nonlocal nonlocal_variable
nonlocal_variable = "changed"
def get_nonlocal_variable():
return nonlocal_variable
return inner_function, get_nonlocal_variable
>>> inner_function, get_nonlocal_variable = function()
>>> get_nonlocal_variable()
1
>>> inner_function()
>>> get_nonlocal_variable()
'changed'