print语句如何创建局部变量

时间:2014-01-13 12:08:48

标签: python python-internals

问题在这篇文章的末尾。

第一个片段:空本地变量字典。

def outer():
    x = 1
    def inner():
        print "Local variables: %s" % locals()
    return inner()
print outer()

输出:     局部变量:{}

第二个片段:在inner()函数内打印并创建局部变量条目。

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
    return inner()
print outer()

输出:

1
Local variables: {'x': 1}

第三个代码段:内部函数内部的del x:

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
        del x
    return inner()
print outer()

输出:

>>> outer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in outer
  File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'x' referenced before assignment
>>>

问题:

  1. 在Second Snippet中,print语句如何创建局部变量。
  2. 如果它在内部函数中创建局部变量,为什么我无法删除它。
  3. 请有人帮我理解这一点。

3 个答案:

答案 0 :(得分:23)

在Python中,除非另外指定(使用global语句或3.0+中的nonlocal语句),如果修改变量,则变量在locals中(赋值给它) ,del等,在函数的任何地方。*

在第一个代码段中,您永远不会修改x,甚至不会访问它,因此它不是本地代码。事实上,它甚至不存在。这很容易。

第二个版本是棘手的。 x不在inner的本地,因为您不会在inner中修改它。因此,Python会寻找它,按范围向外移动范围,直到找到具有该变量的范围。它在outer中将其视为局部变量。这意味着它是inner中的闭包变量自由变量。由于locals函数包括闭包变量和局部变量,因此您可以看到它。

通过del x,第三个版本将x置于inner的本地。**因此,它出现在locals中。但是,你尝试print它没有分配任何东西,所以没有任何价值。所以你得到UnboundLocalError

通常,一旦你理解了Python在这里尝试完成的基本思想,通常很明显你拥有什么样的变量。但如果不清楚,详细规则在Naming and Binding中定义。


如果您想了解封面如何工作,您可以从检查功能对象开始。试试这个:

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
    return inner
inner = outer()
print inner.func_closure
print inner.func_code.co_freevars
print outer.func_code.co_cellvars

inspect模块文档列出了functioncode以及其他“幕后”对象的所有重要成员。

使用dis模块查看outerinner的字节码也可能会有所帮助。***例如,如果您运行this code,那么'我会看到本地LOAD_FAST,单元格LOAD_DEREF和全局LOAD_GLOBAL

但是,如果你真的想要了解所有这些是如何运作的,那么在{Eli Bendersky的“Python内部”博客上的symbol tables上的一系列文章几乎涵盖了所有内容。 (感谢Ashwini Chaudhary找到它并在评论中指出它。)


*这是在编译时检查的,而不是执行时间,所以试图将它与例如exec混淆可能会成功地混淆Python和你自己。

**请注意,del同时作为修改和访问。这可能会令人惊讶,但您可以看到def foo(): del x会引发UnboundLocalError因为del使x成为本地,而del无法找到{{1}}价值。

*** ...假设你正在使用一个使用CPython样式字节码的Python实现,比如CPython本身(当然)或PyPy。

答案 1 :(得分:8)

Python通过查看在编译时如何使用变量来支持嵌套作用域。您在函数中分配的变量(或在函数中与import绑定)被视为本地变量,其他所有变量都是非本地变量。尝试删除变量也会将其标记为本地变量。

在父作用域中搜索非本地名称,如果未找到则被视为全局名称。

在第二个示例中,x引用父作用域中的名称。您没有分配它,因此它是一个嵌套名称,可以在本地命名空间中看到。它实际上不是 本地名称,而是一个自由变量;它的值取自父范围。

在上一个示例中,您尝试删除x,使其成为本地名称。在分配任何内容之前尝试引用它会导致异常。

这些都记录在Python参考的Execution model文档中。具体做法是:

  

在代码块中使用名称时,使用最近的名称解析   封闭范围。代码块可见的所有此类范围的集合是   称为块的环境

     

如果名称绑定在块中,则它是该块的局部变量。   如果名称在模块级别绑定,则它是全局变量。 (该   模块代码块的变量是本地的和全局的。)如果是   变量在代码块中使用但在那里没有定义,它是 free   变量

     

以下构造绑定名称:形式参数到函数,   import语句,类和函数定义(这些绑定   定义块中的类或函数名称)和目标   标识符,如果出现在赋值中,for循环标题中   except子句标题的第二个位置或者as之后的第二个位置   声明。 import形式的from ... import *语句绑定   导入模块中定义的所有名称,除了那些以   一个下划线。此表单只能在模块级别使用。

     

del语句中出现的目标也被视为绑定   这个目的(虽然实际的语义是取消绑定名称)。它   取消绑定封闭范围引用的名称是非法的;   编译器将报告SyntaxError

答案 2 :(得分:0)

在python docs faq页面上有一个解释Why am I getting an UnboundLocalError when the variable has a value?,类似于abarnert和Martijn的上述答案。