Python - 在以前在全局范围内查找的函数内重新分配名称

时间:2017-04-05 12:45:03

标签: python

为什么我在下面的第3个代码中出现错误,但在前两个代码中却没有?我使用的是Python 3.6.0,Anaconda 4.3.1(64位)Jupyter。

代码1:

c = 100
def fib():
    c = 20
    a = c
    print ("a in fib", a)
    print ("c in fib", c)
fib()
print ("c", c)

代码1的输出:

a in fib 20
c in fib 20
c 100

代码2:

c = 100
def fib():
    a = c
    print ("a in fib", a)
    print ("c in fib", c)   
fib()
print ("c", c)

代码2的输出:

a in fib 100
c in fib 100
c 100

代码3:

c = 100
def fib():
    a = c
    print ("a in fib", a)
    print ("c in fib", c)
    c = 20  
fib()
print ("c", c)

代码3的输出:

UnboundLocalError                         Traceback (most recent call last)
<ipython-input-42-d6affa26dc65> in <module>()
      7 
      8 
----> 9 fib()
     10 print ("c", c)

<ipython-input-42-d6affa26dc65> in fib()
      1 c = 100
      2 def fib():
----> 3     a = c
      4     print ("a in fib", a)
      5     print ("c in fib", c)

UnboundLocalError: local variable 'c' referenced before assignment

7 个答案:

答案 0 :(得分:4)

此行为称为词法作用域。以下是官方Python docs(强调我的)的相关部分:

  

如果名称绑定操作发生在代码块中的任何位置,则全部   块中名称的使用被视为对的引用   当前阻止。这可能会导致在a中使用名称时出错   在绑定之前阻止。这条规则很微妙。 Python缺乏   声明并允许名称绑定操作发生在任何地方   在代码块中。 代码块的局部变量可以是   通过扫描块的整个文本来确定名称绑定   操作

关于"name binding operations"的定义(再次强调我的):

  

名称指的是对象。名称通过名称绑定引入   操作

     

以下构造绑定名称:形式参数到函数,   gcc语句,类和函数定义(这些绑定   定义块中的类或函数名称,以及目标   标识符,如果出现在分配import循环标题中,或之后   for语句或as条款中的withexcept的{​​{1}}陈述   form import绑定导入模块中定义的所有名称,   除了以下划线开头的那些。此表格仅可使用   在模块层面。

     

from ... import *语句中出现的目标也被视为绑定   这个目的(虽然实际的语义是取消绑定名称)。

因此,在您的第3个代码示例中,块末尾的del通过“作为标识符的目标(如果在赋值中发生)”条款限定为名称绑定操作。这使得该函数中出现的所有c = 20都引用了 local 变量c。但是,在运行时,您的代码首先会遇到c行。由于a = c实际上尚未在本地范围内定义,因此您将获得例外。

在第一个块中,您在尝试引用它之前在本地定义c,所以没有问题。

在第二种情况下,您不会在函数中对c执行任何名称绑定操作,因此Python假定您需要外部定义。

至于为什么以这种方式运作,请参阅this question

  

词法范围的基本原理不仅仅是表现(完整的词法范围,包括闭包实际上有性能成本,请参见funcarg问题),这是为了简单和可靠。虽然在第一次学习语言时可能会令人惊讶,但规则实际上很简单,因此有经验的程序员可以立即知道标识符的哪个用途指的是哪个范围。人们可以孤立地理解函数,因为执行不受调用此函数的人以及他们如何决定命名变量的影响。

答案 1 :(得分:1)

这是您正在寻找的official explanation

import os 
os.system('exiftool -h image.jpg')
  

这是因为当您对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量。由于foo中的最后一个语句为x分配了一个新值,编译器会将其识别为局部变量。因此,当较早的print(x)尝试打印未初始化的局部变量并产生错误时。

答案 2 :(得分:1)

那是一个有趣的角落案例。原因来自Python将变量绑定到块的方式:

  

4.2.1。名称的绑定
  ...
  以下构造绑定名称:...作为标识符的目标,如果在赋值中发生....   如果名称绑定在块中,则它是该块的局部变量,除非声明为非本地或全局。

在第一个块中,c是函数的本地函数,在使用前分配:fine和全局C在fib()函数调用中保持不变

在第二个块c中未在函数定义中指定:a=c使用全局变量

第三,在集团内部分配c,因此它是本地的。但它在分配之前使用,因此错误。如果要使用全局值,则必须将其声明为全局值:

def fib():
    global c
    a = c
    print ("a in fib", a)
    print ("c in fib", c)
    c = 20

答案 3 :(得分:0)

我无法肯定地说,但我认为最可能的解释是前两个看到变量的全球版本&#39; c。

第三个必须看到你定义一个局部变量&#39; c&#39;在&#39;底部?功能,所以不使用全球&#39; c&#39;变量。实际上,它是一个很好的例子,说明为什么你不应该以这种方式使用全局变量。

答案 4 :(得分:0)

Python“假设”我们想要一个局部变量,因为赋值给了fib()的内部,所以第一个print语句抛出了这个错误信息。在函数内部更改或创建的任何变量都是本地的,如果它尚未声明为全局变量。要告诉Python,我们要使用全局变量,我们必须使用关键字“global”,如以下示例所示:

c = 100
def fib():
    global c
    a = c
    print ("a in fib", a)
    print ("c in fib", c)
    c = 20  
fib()
print ("c", c)

答案 5 :(得分:0)

当我们使用全局变量时,我们从根本上使它可用于我们已定义或计划定义的所有函数。在你说的代码中 c=100 您定义了一个全局变量。但是要在函数内使用它而不被局部变量隐藏,你可以这样做

def fib():
   global c
   print(c)
   c=10
   a=c
   print(a,c)

fib()

Output: 100 10 10

局部变量需要在同一范围内引用定义。

答案 6 :(得分:-1)

这是因为您可以在Python中使用但不能更改字符串值(一旦分配)。

  

您可以通过(重新)将变量分配给另一个字符串来“更新”现有字符串。 [ref]

在您的方案中,您使用变量c并将其分配给a,但您必须进一步使用新创建的变量a,而不是更改c