【问题标题】:nested function change variable in an outside function not working外部函数中的嵌套函数更改变量不起作用
【发布时间】:2017-05-07 19:46:20
【问题描述】:
def some_func(a): 
    def access_a():
        print(a)
    access_a()

输出a 的值。但是,如果我想像这样在嵌套函数中更改a

def some_func(a): 
    def change_a():
        a += 1
        print(a)
    change_a()

它引发UnboundLocalError 异常。

我知道a是一个非局部变量,但是为什么我可以不声明nonlocal a就访问它呢?

【问题讨论】:

  • 你在函数中试过global a吗?
  • 这和global是一样的:你可以访问一个全局变量,但是在没有global var的情况下尝试修改它会失败。
  • 不管有什么方法可以解决这个问题,像这样伸出手并导致任意副作用都不是好的编程习惯。
  • ...所以你一开始就不应该这样做。

标签: python python-3.x nested python-nonlocal


【解决方案1】:

Python 范围规则 101:

  1. 除非明确声明 global(Python 2.x 和 3.x)或 nonlocal(仅限 Python 3.x),否则绑定在函数体中的名称被视为 local。这适用于函数体中发生赋值的任何地方。在绑定之前尝试读取局部变量当然是错误的。

  2. 1234563注意:函数参数实际上是本地名称,因此永远不会在封闭范围内查找它们。

请注意,a += 1 主要是 a = a + 1 的快捷方式,因此在您的示例中,a 是本地的(绑定在函数的主体中,没有明确声明为全局或非本地),但您尝试阅读它(rhs a = a+1) 在绑定之前。

在 Python 3 中,您可以使用 nonlocal 语句解决此问题:

>>> def outer(a):
...    def change():
...       nonlocal a
...       a += 1
...    print("before : {}".format(a))
...    change()
...    print ("after : {}".format(a))
... 
>>> outer(42)
before : 42
after : 43

Python 2 没有 nonlocal,因此规范的技巧是将变量包装在可变容器中(通常是 list,但任何可变对象都可以):

>>> def outer(a):
...     _a = [a]
...     def change():
...         _a[0] += 1
...     print("before : {}".format(_a[0]))
...     change()
...     print ("after : {}".format(_a[0]))
... 
>>> outer(42)
before : 42
after : 43

这至少可以说是相当丑陋的。

虽然闭包非常方便,但它们大多是对象的功能对应物:一种在一组函数之间共享状态同时保留该状态封装的方法,所以如果你发现你需要一个 nonlocal 变量也许一个适当的类可能是一个更清洁的解决方案(尽管可能不适用于不返回内部函数但仅在内部使用它的示例)。

【讨论】:

    【解决方案2】:

    我有两个解决方案给你:

    #first one:
    # try with list, compound data types dict/list
    def some_func(a): 
        def change_a():
            a[0] += 1
            print(a[0])
        change_a()
    some_func([1])
    >>> 2
    
    
    #second one
    #reference pointer 
    from ctypes import *
    def some_func_ctypes(a):
        def change_a():
          a[0] += 1
          print a.contents, a[0]
        change_a()
    
    i = c_int(1)
    pi = pointer(i)
    some_func_ctypes(pi)
    
    >>> c_int(2) 2
    

    【讨论】:

    • OP 问的是为什么他可以读取一个非局部变量但不能重新绑定它...
    • @bruno,伙计,你做了很好的解释/回答,谢谢。我应该再解释一遍吗?我想它不再需要,但其他解决方案对未来的读者来说会很棒......
    【解决方案3】:

    当您使用+= 运算符时,会为a 分配一个新值。这在口译员的眼中变成了当地人。

    【讨论】:

      猜你喜欢
      • 2011-09-06
      • 2014-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-05
      相关资源
      最近更新 更多