Python非本地语句


340

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

官方Python网站上没有文档,help("nonlocal")也无法使用。



18
这是非本地的官方Python网站文档:docs.python.org/3/reference/…(此文档自Python 3.0起可用,因此OP的说法是没有官方文档是错误的)
wkschwartz

3
"There is no documentation for nonlocal".实际上,您可以help(keyword_in_string)使用Python 3及更高版本进行文档处理
ytpillai

10
公平的说,官方文档对这个主题很烂。所选答案的示例非常清楚,这是一个有价值的问题。
疯狂物理学家,2016年

Answers:


471

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

32
与全局x有何不同?
ooboo

52
它非常相似-但是请注意,外部x在示例中不是全局的,而是在外部函数中定义的。
匿名2009年

3
@Dustin-实际上,如果您的A类具有属性x和其中定义的子类B,则可以将B中的x称为Ax
Anon

2
定义内部函数时,代码很容易缩进,最终违反了79个字符的PEP8建议。有什么办法解决这个问题?内部功能可以以某种方式放置在外部功能之外吗?我知道这个问题听起来很愚蠢,但我很认真。
tommy.carstensen 2015年

3
@ tommy.carstensen您可以将函数作为arg传递,这是高阶函数的优点。在函数式编程中,这也称为组合,python不是纯FP语言,但您当然可以使用功能(生成器,高阶函数是一些示例)
superuseroi 2015年

90

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


41

谷歌搜索“ 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
我确定这就是关键字'global'的作用-达到更高的环境,直到到达具有该名称的变量为止。变量x可以在模块级别的类中声明,然后在该类内部的函数中声明,然后在该函数的内部函数中声明-如何知道要引用哪个x?
ooboo

7
关于global的事情是它仅适用于global变量。它看不到封闭的非全局范围内的变量。
SingleNegationElimination

我尝试了make_counter-但是它不返回生成器而是函数。有没有办法退还发电机,以便以后我可以迭代呢?
Dejell 2013年

@Dejel:此示例旨在说明nonlocalPython中的语句;如果您想要一个自然数序列,则python惯用法实际上是itertools.count()
SingleNegationElimination

我想演示一下像yield一样返回生成器的功能-yield实际上返回了生成器。我的想法不是使用yield,而是使用非本地或其他解决方案
Dejell 2013年

15

@ooboo:

它与源代码中的参考点“最接近”。这称为“词法作用域”,现在已经有40多年的历史了。

Python的类成员确实在名为的字典中,__dict__并且无法通过词法作用域来访问。

如果您未指定,nonlocal而是这样做x = 7,它将创建一个新的局部变量“ x”。如果您指定nonlocal,它将找到“最近”“ x”并分配给它。如果指定nonlocal并且没有“ x”,它将给您一条错误消息。

关键字global在我看来一直很奇怪,因为它会很乐意忽略除最外面的一个以外的所有其他“ x”。奇怪的。


14

help('nonlocal')nonlocal语句


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

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

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

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

也可以看看:

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

相关帮助主题:全局,NAMESPACES

资料来源:Python语言参考


11
每天学些新东西。我不知道您可以help()在关键字上使用(现在我很震惊:help()没有参数可以交互)。
Erik Youngren

6

引用《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

3
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)

2

我对“非本地”语句的个人理解(并且对不起,因为我是Python和程序设计的新手)所以,“非本地”是在迭代函数中使用全局功能的一种方式,而不是代码本身。 。如果愿意,可以在函数之间进行全局声明。


0

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

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.