我对下面的代码片段中的全局关键字行为感到困惑,我期望所有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(或外部作用域),而反映在全局名称空间中。
一个常见的缩写是LEGB:
这是Python搜索名称空间以查找变量分配的顺序。
本地名称空间是当前代码块中发生的所有事情。函数定义包含局部变量,它们是Python查找变量引用时首先发现的。在这里,Python将首先查看foo
的本地范围,找到分配为x
的2
并将其打印出来。尽管在全局名称空间中也定义了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]
您可以使用关键字
nonlocal
和global
强制将变量设置为非局部变量(注意: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
:检查变量的最终参考。您可以重载本机Python函数(但请不要这样做,这是个坏主意)。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将检查内置关键字。诸如
list
和int
之类的本地Python函数是Python检查AFTER
这里是您不应该做的事的例子。在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中的名称空间和闭包。
全局语句是一个声明,它适用于整个当前代码块。这意味着列出的标识符是解释为全局变量。
注意,它仅适用于当前代码块。因此,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)
只需在outer_function中取消注释您的全局命令,否则您将声明一个值为20的局部变量,先更改全局变量,然后再打印相同的局部变量。
使用全局变量不是一个好主意。如果只想重置变量的值,则只需使用以下行: