“非本地”在 Python 3 中有什么作用?

What does "nonlocal" do in Python 3?

提问人:ooboo 提问时间:8/12/2009 最后编辑:Super Kai - Kazuya Itoooboo 更新时间:2/3/2023 访问量:167253

问:

Python 3.x 中有什么作用?nonlocal


要关闭 OP 需要非本地且未实现的调试问题,请使用 是否可以在 python 中修改外部但非全局范围内的变量?

尽管自 2020 年 1 月 1 日起正式不支持 Python 2,但如果由于某种原因您被迫维护 Python 2.x 代码库并需要与 nonlocal 等效的代码库,请参阅 Python 2.x 中的 nonlocal 关键字

闭包 全局 嵌套函数 python-nonlocal

评论

4赞 Matt Joiner 12/5/2010
看看这个问题: stackoverflow.com/questions/1414304/local-functions-in-python
27赞 wkschwartz 10/4/2013
以下是非本地的官方 Python 网站文档:docs.python.org/3/reference/... (此文档自 Python 3.0 以来一直可用,因此 OP 断言没有官方文档是错误的)
4赞 rassa45 8/7/2015
"There is no documentation for nonlocal".实际上,您可以为 Python 3 及更高版本的文档做help(keyword_in_string)
17赞 Mad Physicist 5/28/2016
公平地说,官方文档对这个主题有点吝啬。所选答案的例子把事情说得很清楚,使这是一个有价值的问题。
1赞 jammon 10/8/2016
在官方的 Python 教程中,有一个很好的例子作用域和命名空间的概念进行了很好的解释

答:

125赞 Arkady 8/12/2009 #1

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

55赞 SingleNegationElimination 8/12/2009 #2

在谷歌上搜索“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,但对于初学者来说,第一个版本似乎会更明显一些。通过调用返回的函数来正确使用生成器是一个常见的混淆点。第一个版本显式返回一个函数。

评论

1赞 ooboo 8/12/2009
我确信这就是关键字“global”的作用 - 在更高的环境中工作,直到它到达具有该名称的变量。变量 X 可以在模块级别、类内声明,然后分别在类内的函数中声明,然后在该函数的内部函数中声明——它如何知道要引用哪个 X?
16赞 SingleNegationElimination 10/11/2009
关于全局的问题是它只适用于全局变量。它无法查看封闭的非全局范围内的变量。
0赞 Dejell 12/6/2013
我尝试了make_counter - 但是它没有返回生成器,而是返回函数。有没有办法返回生成器,以便以后我可以迭代它?
0赞 SingleNegationElimination 12/6/2013
@Dejel:此示例旨在说明 Python 中的语句;如果你想要一个自然数序列,python 的习语实际上是 itertools.count()nonlocal
0赞 Dejell 12/6/2013
我想演示一下像 yield 一样返回生成器的能力 - yield 实际上返回一个生成器。我的想法是不使用产量,而是使用非本地或其他解决方案
643赞 Anon 8/12/2009 #3

比较一下,不使用: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

为此,使用非本地的,其中 's 现在也是 's:inner()xouter()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,它将绑定到正确的“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

评论

63赞 Anon 8/12/2009
它非常相似 - 但请注意,外部 x 在示例中不是全局的,而是在外部函数中定义的。
3赞 Anon 8/12/2009
@Dustin - 实际上,如果您有具有属性 x 的类 A 和定义了子类 B,则会将 B 中的 x 称为 A.x
3赞 tommy.carstensen 2/12/2015
在定义内部函数时,代码很容易大量缩进,最终违反了 79 个字符的 PEP8 建议。有什么方法可以解决这个问题吗?一个内在的功能可以以某种方式放在外在功能之外吗?我知道这个问题听起来很愚蠢,但我是认真的。
5赞 superuseroi 3/31/2015
@tommy.carstensen,你可以将函数作为参数传递,这就是高阶函数的美妙之处。同样在函数式编程中,这被称为组合,python 不是一种纯粹的 FP 语言,但你当然可以使用功能(生成器、高阶函数是一些例子)
7赞 Rastapopoulos 10/9/2020
如果有 3 个嵌套函数怎么办?那么有没有办法从最里面的函数中访问所有 4 个级别呢?
24赞 Danny Milosavljevic 12/5/2010 #4

它采用“最接近”源代码中引用点的那个。 这被称为“词汇范围界定”,现在是 >40 年的标准。

Python 的类成员实际上位于一个名为的字典中,并且永远不会通过词法范围到达。__dict__

如果你不指定但做,它将创建一个新的局部变量 “x”。 如果您指定了 ,它将找到“最接近的”“x”并分配给它。 如果指定并且没有“x”,它将为您提供错误消息。nonlocalx = 7nonlocalnonlocal

这个关键字对我来说总是很奇怪,因为它会很乐意忽略除了最外面的那个之外的所有其他“x”。global

评论

1赞 Nearoo 11/14/2021
请注意,如果您分配新值,而只读取它(例如),则词法范围是默认的,没有区别。print(x)nonlocal
0赞 Karl Knechtel 9/5/2022
“关键字global对我来说总是很奇怪,因为它会很乐意忽略所有其他”x“,除了最外面的那个。我不明白其中的道理。最外层的范围是全局的;其他的则不是。为什么要访问它们?global
2赞 Yossi Truzman 8/4/2011 #5

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

17赞 Yossi Truzman 8/4/2011 #6

help('非本地') 声明nonlocal


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

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

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

声明中列出的名称不得与前 本地作用域中的现有绑定。nonlocal

另请参阅:

PEP 3104 - 访问外部作用域
中的名称 语句的规范。
nonlocal

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

来源:Python 语言参考

评论

17赞 Erik Youngren 2/24/2014
每天都能学到新东西。我不知道你可以在关键字上使用(现在我的脑子被炸了:没有参数是交互式的)。help()help()
3赞 gxyd 5/6/2015 #7
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)
0赞 NIPHIN 3/21/2018 #8

具有“非局部”内部函数(即;嵌套内部函数)可以获取外部父函数的特定变量的读和“写”权限。非局部只能在内部函数中使用 例如:

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)
13赞 Jeyekomon 3/21/2018 #9

引用 Python 3 参考

非局部语句使列出的标识符引用最近封闭范围(不包括全局变量)中先前绑定的变量。

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

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
0赞 Super Kai - Kazuya Ito 1/9/2023 #10

文档如下:

非本地语句使列出的标识符引用 最接近的封闭范围内的先前绑定变量,不包括 全局变量。...

例如,in 可以访问非局部变量 foo = 10 in,但不能访问非局部变量 foo = 5 in 或全局变量 foo = 0 outside,如下所示:nonlocal fooinner()middle()outer()outer()

foo = 0 # <- ✖
def outer():
    foo = 5 # <- ✖
    def middle():
        foo = 10 # <- 〇
        def inner():
            nonlocal foo # Here
            foo += 1
            print(foo) # 11
        inner()
    middle()
outer()