Python全局关键字

问题描述 投票:2回答:4

我对下面的代码片段中的全局关键字行为感到困惑,我期望所有3张纸中的30、30、30。

def outer_function():
    #global a  ###commented intentionally
    a = 20
    def inner_function():
        global a
        a = 30
        print('a =',a)

    inner_function()
    print('a =',a)

a = 10
outer_function()
print('a =',a)
#Output:
#30
#20  #Expecting 30 here
#30

所有混乱都来自外部函数定义后的“全局a”。据我目前的理解是,“所有对变量的引用和赋值都会在该变量的全局关键字声明中全局反映”。如果我对第一条全球声明没有评论,我将获得预期的输出30,30,30。

为什么inner_function内部的全局声明和值更改不会反映在第二个输出上,即outer_function(或外部作用域),而反映在全局名称空间中。

python
4个回答
5
投票

一个常见的缩写是LEGB

  • L ocal
  • E nclosed
  • G小叶
  • B渐进式

这是Python搜索名称空间以查找变量分配的顺序。

本地

本地名称空间是当前代码块中发生的所有事情。函数定义包含局部变量,它们是Python查找变量引用时首先发现的。在这里,Python将首先查看foo的本地范围,找到分配为x2并将其打印出来。尽管在全局名称空间中也定义了x,所有这些都发生了。

x = 1
def foo():
    x = 2
    print(x)

foo()
# prints:
2

当Python编译函数时,它决定定义代码块中的每个变量是局部变量还是全局变量。为什么这很重要?让我们看一下foo的相同定义,但是翻转其中的两行。结果可能令人惊讶]]

x = 1
def foo():
    print(x)
    x = 2

foo()
# raises:
UnboundLocalError: local variable 'x' referenced before assignment

发生此错误的原因是,由于分配了x,Python将foo编译为x = 2中的local

变量。

您需要记住的是,局部变量只能访问它们自己范围内的内容。

附上

定义多层函数时,未编译为局部变量的变量将在下一个最高名称空间中搜索其值。这是一个简单的示例。

x = 0

def outer_0():
    x = 1
    def outer_1():
        def inner():
            print(x)

        inner()

    outer_1()

outer_0()
# print:
1

编译inner()时,Python将x设置为全局变量,这意味着它将尝试访问本地范围之外的x的其他分配。 Python在向上移动通过封闭的名称空间时搜索x值的顺序。 x不包含在outer_1的名称空间中,因此它检查outer_0,查找值并将该分配用于x中的inner

x --> inner --> outer_1 --> outer_0 [ --> global, not reached in this example]

您可以使用关键字nonlocalglobal强制将变量设置为非局部变量(注意:nonlocal仅在Python 3中可用)。这些是关于变量范围的编译器指令。

nonlocal

使用nonlocal关键字告诉python将变量分配给找到的第一个实例,因为它在名称空间中向上移动。对变量所做的任何更改也将在变量的原始名称空间中进行。在下面的示例中,当为2分配了x时,它也在x范围内设置outer_0的值。

x = 0

def outer_0():
    x = 1
    def outer_1():
        def inner():
            nonlocal x
            print('inner  :', x)
            x = 2
        inner()
    outer_1()
    print('outer_0:', x)

outer_0()
# prints:
inner  : 1
outer_0: 2

全局

全局名称空间是您的程序在其中运行的最高级别的名称空间。它也是所有函数定义的最高封闭的名称空间。通常,将值传入和传出全局命名空间中的变量不是一个好习惯,因为可能会发生意外的副作用。

global

使用global关键字类似于非本地关键字,但是它不是向上移动名称空间层,而是only

在全局名称空间中搜索变量引用。使用上面相同的示例,但是在这种情况下,声明global x告诉Python在全局名称空间中使用x的分配。这里的全局名称空间有x = 0
x = 0

def outer_0():
    x = 1
    def outer_1():
        def inner():
            global x
            print('inner  :', x)
        inner()
    outer_1()

outer_0()
# prints:
0

同样,如果尚未在全局名称空间中定义变量,它将引发错误。

def foo():
    z = 1
    def bar():
        global z
        print(z)
    bar()

foo()
# raises:
NameError: name 'z' is not defined

内置

最后,Python将检查内置关键字。诸如listint之类的本地Python函数是Python检查AFTER

检查变量的最终参考。您可以重载本机Python函数(但请不要这样做,这是个坏主意)。

这里是您不应该做的事的例子。在dumb中,我们通过将原生Python list函数分配给0范围内的dumb来重载它。在even_dumber中,当我们尝试使用list将字符串分成字母列表时,Python会在list的封闭命名空间中找到对dumb的引用,并尝试使用它,从而引发错误。

def dumb():
    list = 0
    def even_dumber():
        x = list('abc')
        print(x)
    even_dumber()

dumb()
# raises:
TypeError: 'int' object is not callable

您可以使用以下方法引用list的全局定义来恢复原来的行为:

def dumb():
    list = [1]
    def even_dumber():
        global list
        x = list('abc')
        print(x)
    even_dumber()

dumb()
# returns:
['a', 'b', 'c']

但是再次,不要这样做,这是不好的编码习惯。

我希望这有助于揭示一些名称空间在Python中的工作方式。如果您想了解更多信息,Luciano Ramalho撰写的Fluent Python第7章将深入介绍Python中的名称空间和闭包。

documentation

全局语句是一个声明,它适用于整个当前代码块。这意味着列出的标识符是解释为全局变量。

注意,它仅适用于当前代码块。因此,global中的inner_function仅适用于inner_function。在它之外,标识符不是全局的。

请注意,“标识符”与“变量”如何不同。因此,它告诉解释器的内容是“当我在此代码块中使用标识符a时,不应用正常的范围解析,实际上是指模块级变量,”。

只需在outer_function中取消注释您的全局命令,否则您将声明一个值为20的局部变量,先更改全局变量,然后再打印相同的局部变量。

使用全局变量不是一个好主意。如果只想重置变量的值,则只需使用以下行:

def outer_function():
a = 20
def inner_function():
         a = 30
         print('a =',a)
         return a

    a = inner_function()
    print('a =',a)
    return a

a = 10
a = outer_function()
print('a =',a)

2
投票

1
投票

只需在outer_function中取消注释您的全局命令,否则您将声明一个值为20的局部变量,先更改全局变量,然后再打印相同的局部变量。


1
投票

使用全局变量不是一个好主意。如果只想重置变量的值,则只需使用以下行:

热门问题
推荐问题
最新问题