1

当名称未定义和未绑定时,我们可以在运行时看到NameError和。UnboundLocalError但目前尚不清楚名称评估是如何在运行时发生的?我假设如下:

考虑代码片段的示例

def foo():
    a=3
    def bar():
        return a
    tmp=bar()
    res=a+tmp
    return res

bar函数被调用时,我们创建了新的执行框架。将此帧表示为bar_framebar_frame.f_local字典中不包含任何元素。但bar_frame.f_back.f_locals包含 4 个名称-值对。所以

我的理解:我们有以下名称评估算法:

  1. 试图namecurrentframe.f_locals

    1.1 如果currentframe.f_locals对应一个全局命名空间并且没有找到合适的名字则抛出NameError

    1.1 如果找到合适的名称并且有界则返回currentframe.f_locals[name]

    1.2 如果找到合适的名字并且是无限抛出UnboundLocalName错误。

  2. 试图namecurrentframe.f_back.f_locals

请检查我的理解。

4

1 回答 1

2

您描述的算法以一种称为动态作用域的方式运行,其中非本地名称在运行时从调用者的环境中获取。但是 Python 不使用动态范围。它使用词法作用域。这意味着非本地名称是从词法周围的函数中解析出来的。例如,从此函数返回的函数f

def f():
    x = 5
    def g():
        print(x)
    return g

... 总是打印 5,即使它是这样调用的:

def h():
    g = f()
    x = 10
    g()

g称为闭包。哪些名称是非本地的,以及它们引用多“远”(您可以在函数中包含函数),由字节码编译器决定,而不是在运行时决定。字节码包含所有这些信息,因此在运行时,解释器在解析名称时不会搜索任何内容,更不用说多个帧。它总是从所使用的字节码指令中准确地知道去哪里寻找。

附录:框架对象存在的原因,具有所有本地人的名称和值,并引用其调用者的框架,是无关的。存储局部变量是为了便于调试(以及一些涉及影响调用函数的局部变量的讨厌的黑客行为)。f_back存在也是调试和黑魔法,最突出的是堆栈跟踪必须工作。

于 2014-03-01T12:20:58.533 回答