代码之家  ›  专栏  ›  技术社区  ›  Thomas O

具有嵌套函数作用域的UnboundLocalError

  •  19
  • Thomas O  · 技术社区  · 14 年前

    我有这样的代码(简化):

    def outer():
        ctr = 0
    
        def inner():
            ctr += 1
    
        inner()
    

    但是 ctr 导致错误:

    Traceback (most recent call last):
      File "foo.py", line 9, in <module>
        outer()
      File "foo.py", line 7, in outer
        inner()
      File "foo.py", line 5, in inner
        ctr += 1
    UnboundLocalError: local variable 'ctr' referenced before assignment
    

    我怎么修这个?我以为嵌套的作用域会允许我这样做。我试过用“全球”,但还是不行。

    4 回复  |  直到 6 年前
        1
  •  29
  •   Kevin    6 年前

    如果您使用的是python 3,那么可以使用 nonlocal 用于重新绑定非本地名称的语句:

    def outer():
        ctr = 0
    
        def inner():
            nonlocal ctr
            ctr += 1
    
        inner()
    

    如果您使用的是python 2,它没有 非局部的 ,您需要在不重新绑定barename的情况下执行递增(通过将计数器保留为某个barename的项或属性, 作为一个裸名本身)。例如:

    ...
    ctr = [0]
    
    def inner():
        ctr[0] += 1
    ...
    

    当然还有用途 ctr[0] 无论你在哪里赤身裸体 ctr 现在在别处。

        2
  •  6
  •   Aran-Fey Kevin    6 年前

    http://www.devshed.com/c/a/Python/Nested-Functions-in-Python/1/

    嵌套函数体中的代码可以 本地访问(但不重新绑定) 外部函数的变量,也 被称为嵌套的自由变量 功能。

    所以,你需要通过 ctr inner 明确地。

        3
  •  4
  •   Aran-Fey Kevin    6 年前

    解释

    每当一个值被分配给一个函数内的变量时,python就会认为该变量是 局部变量 关于这个功能。因为声明 ctr += 1 包括分配给 ctr ,python认为 点击率 是本地的 inner 功能。因此,它甚至从来没有试图观察 CTR 在中定义的变量 outer .python看到的本质是:

    def inner():
        ctr = ctr + 1
    

    我认为我们都同意这段代码会导致一个错误,因为 点击率 在定义之前正在访问。

    (也见) this question 有关python如何确定变量范围的详细信息。)

    解决方案(在python 3中)

    python 3引入了 nonlocal statement 它的工作原理和 global 语句,但允许我们访问周围函数的变量(而不是全局变量)。简单添加 nonlocal ctr 内部的 功能和问题将消失:

    def outer():
        ctr = 0
    
        def inner():
            nonlocal ctr
            ctr += 1
    
        inner()
    

    解决方法(在Python2中)

    自从 非局部的 python 2中不存在语句,我们必须狡猾。有两种简单的解决方法:

    • 正在删除所有分配给 CTR

      因为python只考虑 CTR 一个局部变量,因为有对该变量的赋值,所以如果删除对该名称的所有赋值,问题就会消失。 CTR . 但是我们如何在不给变量赋值的情况下改变它的值呢?简单:我们将变量包装在一个可变的对象中,比如一个列表。然后我们可以修改这个列表,而不必为名称赋值 CTR :

      def outer():
          ctr = [0]
      
          def inner():
              ctr[0] += 1
      
          inner()
      
    • 经过 CTR 作为一个论点 内部的

      def outer():
          ctr = 0
      
          def inner(ctr):
              ctr += 1
              return ctr
      
          ctr = inner(ctr)
      
        4
  •  -1
  •   Aran-Fey Kevin    6 年前

    申报怎么样 ctr 在外面 outer (即在全局范围内)或任何其他类/函数?这将使变量可访问和可写。