【问题标题】:Python nonlocal statementPython 非本地语句
【发布时间】:2010-11-18 17:06:10
【问题描述】:

Python nonlocal 语句有什么作用(在 Python 3.0 及更高版本中)?

Python 官方网站上没有文档,help("nonlocal") 也不起作用。

【问题讨论】:

标签: python closures global python-nonlocal


【解决方案1】:

引用自Python 3 Reference

nonlocal 语句导致列出的标识符引用之前在最近的封闭范围内绑定的变量,不包括全局变量。

如参考中所述,在多个嵌套函数的情况下,仅修改最近的封闭函数中的变量:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        x = 2
        innermost()
        if x == 3: print('Inner x has been modified')

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Inner x has been modified

“最近的”变量可以在几个级别之外:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Outer x has been modified

但不能是全局变量:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    inner()

x = 0
outer()
if x == 3: print('Global x has been modified')

# SyntaxError: no binding for nonlocal 'x' found

【讨论】:

    【解决方案2】:

    使用“非本地”内部函数(即嵌套内部函数)可以获得外部父函数的特定变量的读取和“写入”权限 。并且 nonlocal 只能在内部函数内部使用 例如:

    a = 10
    def Outer(msg):
        a = 20
        b = 30
        def Inner():
            c = 50
            d = 60
            print("MU LCL =",locals())
            nonlocal a
            a = 100
            ans = a+c
            print("Hello from Inner",ans)       
            print("value of a Inner : ",a)
        Inner()
        print("value of a Outer : ",a)
    
    res = Outer("Hello World")
    print(res)
    print("value of a Global : ",a)
    

    【讨论】:

      【解决方案3】:

      帮助('非本地') nonlocal 声明


          nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*
      

      nonlocal 语句导致列出的标识符引用 以前在最近的封闭范围内绑定变量。这是 很重要,因为绑定的默认行为是搜索 首先是本地命名空间。该语句允许封装的代码 重新绑定全局范围之外的变量 (模块)范围。

      nonlocal 语句中列出的名称,与 global 语句,必须引用预先存在的绑定 封闭范围(应在其中创建新绑定的范围 无法明确确定)。

      nonlocal 语句中列出的名称不得与前 本地范围内的现有绑定。

      另见:

      PEP 3104 - 访问外部范围内的名称
      nonlocal 语句的规范。

      相关帮助主题:全局、命名空间

      来源:Python Language Reference

      【讨论】:

      • 每天学习新东西。我不知道你可以在关键字上使用help()(现在我的想法很震惊:help() 没有参数进入交互式)。
      【解决方案4】:

      比较一下,不使用nonlocal

      x = 0
      def outer():
          x = 1
          def inner():
              x = 2
              print("inner:", x)
      
          inner()
          print("outer:", x)
      
      outer()
      print("global:", x)
      
      # inner: 2
      # outer: 1
      # global: 0
      

      为此,使用nonlocal,其中inner()x 现在也是outer()x

      x = 0
      def outer():
          x = 1
          def inner():
              nonlocal x
              x = 2
              print("inner:", x)
      
          inner()
          print("outer:", x)
      
      outer()
      print("global:", x)
      
      # inner: 2
      # outer: 2
      # global: 0
      

      如果我们使用 global,它会将x 绑定到正确的“全局”值:

      x = 0
      def outer():
          x = 1
          def inner():
              global x
              x = 2
              print("inner:", x)
      
          inner()
          print("outer:", x)
      
      outer()
      print("global:", x)
      
      # inner: 2
      # outer: 1
      # global: 2
      

      【讨论】:

      • 它非常相似 - 但请注意,外部 x 在示例中不是全局的,而是在外部函数中定义的。
      • @Dustin - 实际上,如果你有一个类 A,其中定义了一个属性 x 和一个子类 B,你会在 B 中将 x 称为 A.x
      • 代码在定义内部函数时很容易缩进,最终违反了 79 chars PEP8 建议。有什么办法可以解决这个问题?内部函数可以以某种方式放置在外部函数之外吗?我知道这个问题听起来很愚蠢,但我是认真的。
      • @tommy.carstensen 您可以将函数作为 arg 传递,这是高阶函数的美妙之处。同样在函数式编程中,这被称为组合,python 不是纯粹的 FP 语言,但你当然可以使用特性(生成器,高阶函数就是一些例子)
      • 如果有3个嵌套函数怎么办?那么有没有办法从最里面的函数中访问所有 4 个级别?
      【解决方案5】:
      a = 0    #1. global variable with respect to every function in program
      
      def f():
          a = 0          #2. nonlocal with respect to function g
          def g():
              nonlocal a
              a=a+1
              print("The value of 'a' using nonlocal is ", a)
          def h():
              global a               #3. using global variable
              a=a+5
              print("The value of a using global is ", a)
          def i():
              a = 0              #4. variable separated from all others
              print("The value of 'a' inside a function is ", a)
      
          g()
          h()
          i()
      print("The value of 'a' global before any function", a)
      f()
      print("The value of 'a' global after using function f ", a)
      

      【讨论】:

        【解决方案6】:

        我个人对“非本地”语句的理解(请原谅,因为我是 Python 和一般编程的新手)是“非本地”是在迭代函数中使用全局功能而不是函数体的一种方式代码本身。函数之间的全局语句,如果你愿意的话。

        【讨论】:

          【解决方案7】:

          @ooboo:

          它需要一个“最接近”源代码中的引用点。 这被称为“词法作用域”,40 多年来一直是标准。

          Python 的类成员实际上位于一个名为 __dict__ 的字典中,并且永远无法通过词法作用域访问。

          如果你不指定nonlocal 而指定x = 7,它将创建一个新的局部变量“x”。 如果您确实指定了nonlocal,它将找到“最接近的”“x”并分配给它。 如果你指定nonlocal 并且没有“x”,它会给你一个错误信息。

          关键字global 对我来说一直很奇怪,因为它会很高兴地忽略除最外面的“x”之外的所有其他“x”。很奇怪。

          【讨论】:

          • 请注意,如果您分配一个新值,而只读取它(例如print(x)),则词法范围是默认值,nonlocal 不会区别。
          【解决方案8】:

          谷歌搜索“python nonlocal”找到了提案PEP 3104,它完整地描述了语句背后的语法和推理。简而言之,它的工作方式与global 语句完全相同,只是它用于引用既不是全局变量也不是函数局部变量。

          下面是一个简短的示例,说明您可以使用它做什么。可以重写计数器生成器以使用它,使其看起来更像是带有闭包的语言的惯用语。

          def make_counter():
              count = 0
              def counter():
                  nonlocal count
                  count += 1
                  return count
              return counter
          

          显然,您可以将其编写为生成器,例如:

          def counter_generator():
              count = 0
              while True:
                  count += 1
                  yield count
          

          但是虽然这是完全地道的python,但似乎第一个版本对于初学者来说会更明显一些。通过调用返回的函数来正确使用生成器是一个常见的混淆点。第一个版本显式返回一个函数。

          【讨论】:

          • 我确信这就是关键字“global”的作用——在更高的环境中工作,直到它到达具有该名称的变量。变量 x 可以在模块级别、类内部声明,然后在该类内部的函数中单独声明,然后在该函数的内部函数中声明 - 它如何知道要引用哪个 x?
          • 关于全局的事情是它只适用于全局变量。它无法在封闭的非全局范围内看到变量。
          • 我尝试了 make_counter - 但是它不返回生成器,而是返回一个函数。有没有办法返回一个生成器,以便稍后我可以迭代它?
          • @Dejel:此示例旨在说明 Python 中的 nonlocal 语句;如果要自然数序列,python成语其实是itertools.count()
          • 我想演示像 yield 一样返回生成器的能力——yield 实际上返回一个生成器。我的想法是不使用 yield 而可能使用非本地或其他解决方案
          【解决方案9】:

          简而言之,它允许您将值分配给外部(但非全局)范围内的变量。有关所有血腥细节,请参阅PEP 3104

          【讨论】:

            猜你喜欢
            • 2011-07-24
            • 2010-12-14
            • 2018-04-24
            • 1970-01-01
            • 2018-12-19
            • 1970-01-01
            相关资源
            最近更新 更多