2014-01-13 40 views
22

问题是在这篇文章的末尾。如何打印语句创建一个局部变量

第一个片段:空局部变量字典。

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

输出: 局部变量:{}

第二片段:打印内()函数内部和创建本地变量条目。

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

输出:

1 
Local variables: {'x': 1} 

第三片段

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从内函数内部德尔X ons:

  1. 在第二个片段中,print语句如何创建局部变量。
  2. 如果它在内部函数中创建局部变量,为什么我不能删除它。

有人可以帮我理解这一点。

+1

快速搜索揭示了一个重复的问题,但它的答案不是很好。答案中的一些信息完全错误。我没有投票结束。 – user2357112

+4

从[Python文档](http://docs.python.org/2/library/functions.html#locals):“自由变量由['locals()']返回(http://docs.python .org/2/library/functions.html#locals)在功能块中调用时,不在类块中调用。“ – user2357112

+0

可能的重复[Python简介范围规则](http://stackoverflow.com/questions/291978/short-description-of-python-scoping-rules) –

回答

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,你会看到,小区A LOAD_FAST本地,LOAD_DEREFLOAD_GLOBAL为全球。但是如果你真的想了解所有这些真的如何工作,Eli Bendersky的“Python内部”博客文章symbol tables的一系列文章几乎涵盖了所有内容。 (感谢阿什维尼·乔杜里定位,并在注释中指出来。)


*这是在编译期进行检查,没有执行时间,所以试图与混淆,例如,exec可以成功地混淆两者Python和你自己。

**请注意,del既是修改又是访问。这可能令人惊讶,但是您可以看到def foo(): del x将增加UnboundLocalError,因为del使本地为x,并且与del非常相似无法找到值。

*** ...假设您使用的是使用CPython风格的字节码的Python实现,如CPython本身(当然)或PyPy。

+0

即使我没有打印,当我尝试删除时也得到了同样的错误。 –

+0

@yopy:在这种情况下,您在创建它之前试图删除一个变量,这是一个不同的错误,但不是_that_不同。你可以看到没有任何这种花哨的东西:'def foo():del x'。 – abarnert

+1

@Cilyan:'inner'是封闭的。他立即调用闭包并返回其值的事实,而不是返回闭包本身,并不意味着它不是闭包。 – abarnert

8

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

在父范围内搜索非本地名称,如果未找到则视为全局。

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

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

这全部记录在Python参考的Execution model文档中。具体如下:

当在代码块中使用名称时,将使用最接近的 封闭范围解决该问题。代码块可见的所有这些范围的集合是 ,称为块的环境

如果一个名称被绑定在一个块中,它就是该块的局部变量。 如果名称在模块级绑定,它是一个全局变量。 (模块代码块的 变量是本地和全局变量。)如果在代码块中使用了变量 ,但在此处未定义,则变量为,变量为

以下构建体结合的名称:函数的形式参数, import语句,类和函数定义(这些结合在定义块中 类或函数名),并且是 标识符如果在分配中发生的目标, for循环标题, except子句标题的第二个位置或as与 之间的一个语句。 from ... import *形式的import声明绑定 导入模块中定义的所有名称,但以 开头的下划线除外。该表格只能用于模块级别。

发生在del语句中的目标也被认为绑定为 (尽管实际的语义是解除绑定名称)。它 是非法的,以解除封闭范围引用的名称; 编译器会报告一个SyntaxError