如果你熟悉Python或任何其他编程语言, 你肯定会知道必须先定义变量, 然后才能在程序中使用它们。根据定义的方式和位置, 必须以不同的方式访问变量。有些变量是全局定义的, 有些是局部定义的。这意味着引用程序某个部分中的实体的变量可能引用程序另一部分中的不同内容。
在本教程中, 你将了解:
- 变量的本质
- 在Python函数内部或外部定义变量之间的区别
- 全局关键字
- 非本地关键字
- Python中的闭包
- LEGB规则
如果你想了解有关Python编程的更多信息, 则绝对应该看一下我们免费的Python数据科学入门课程。它涵盖了从变量和计算到列表, 函数和包的所有基础知识。
到底是什么变量?
要了解变量的范围, 重要的是首先了解什么是变量。本质上, 它们是内存中对象的引用或指针。当为实例分配带有=的变量时, 就是将变量绑定(或映射)到该实例。可以将多个变量绑定到同一实例。
Python使用名称空间跟踪所有这些映射。这些是用于将变量名映射到对象的容器。你可以将它们视为词典, 其中包含name:object映射。这允许通过你选择分配给它们的名称访问对象。
在下面的示例中, i首先绑定到整数5。在这种情况下, i是变量名, 而整数值5是对象。
然后, 将j设置为等于i。这实际上意味着j现在绑定到与i相同的整数值, 即5。
如果随后将i更改为等于3, 则经验不足的程序员可能会期望j现在也等于3, 但事实并非如此。 j仍然绑定(或指向)整数值5。唯一改变的是i, 现在绑定到整数值3。
i = 5
j = i
i = 3
print("i: " + str(i))
print("j: " + str(j))
i: 3
j: 5
上面代码的名称空间看起来像{i:3, j:5}, 而不是你期望的{i:3, j:i}。
在Python函数内部或外部定义变量之间的区别
如果在脚本顶部定义变量, 则它将是全局变量。这意味着可以从脚本中的任何位置(包括从函数内部)进行访问。看下面的示例, 其中a是全局定义的。
a = 5
def function():
print(a)
function()
print(a)
5
5
在下一个示例中, 在函数中将a全局定义为5, 但再次将其定义为3。如果从函数中打印a的值, 则将打印本地定义的值。如果在函数外部打印, 则将打印其全局定义的值。在function()中定义的a实际上是与外界隔离的。只能从同一功能内本地访问。因此, 两个a有所不同, 具体取决于你从何处访问它们。
a = 5
def function():
a = 3
print(a)
function()
print(a)
3
5
这一切都很好, 但是这意味着什么呢?
好吧, 假设你有一个记住名称的应用程序, 也可以使用change_name()函数对其进行更改。 name变量是全局定义的, 并且在函数内部定义。如你所见, 该函数无法更改全局变量。
name = 'Théo'
def change_name(new_name):
name = new_name
print(name)
change_name('Karlijn')
print(name)
Théo
Théo
幸运的是, global关键字可以提供帮助。
全局关键字
使用global时, 你要告诉Python使用全局定义的变量, 而不是局部定义它。要使用它, 只需键入global, 然后输入变量名。在这种情况下, 现在可以通过change_name()更改全局变量名称。
name = 'Théo'
def change_name(new_name):
global name
name = new_name
print(name)
change_name('Karlijn')
print(name)
Théo
Karlijn
非本地关键字
非本地语句在嵌套函数中很有用。它使变量引用最近的封闭范围中的先前绑定的变量。换句话说, 它将阻止变量首先尝试在本地进行绑定, 并迫使其进入”更高级别”。
看一下下面的三个代码示例。在第一个中, inner()将x绑定到” c”, external()将x绑定到” b”, 并且x全局定义为” a”。根据从何处访问变量, 将返回不同的绑定。
x = "a"
def outer():
x = "b"
def inner():
x = "c"
print("from inner:", x)
inner()
print("from outer:", x)
outer()
print("globally:", x)
inner: c
outer: b
global: a
使用nonlocal关键字, 你是在告诉python inner()函数中的x实际上应该引用在external()函数中定义的x, 后者高一级。从结果中可以看到, inner()和outer()中的x都定义为” c”, 因为inner()可以访问它。
x = "a"
def outer():
x = "b"
def inner():
nonlocal x
x = "c"
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
inner: c
outer: c
global: a
但是, 如果使用global, 则inner()中的x将引用全局变量。那将被更改, 而不是external()中的那个, 因为你只引用了全局x。你实质上是在告诉Python立即进入全局范围。
x = "a"
def outer():
x = "b"
def inner():
global x
x = "c"
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
inner: c
outer: b
global: c
Python中的闭包
闭包是函数对象, 即使在内存中不再存在值时, 它们也会记住范围内的值。
请记住, 嵌套函数是在另一个函数中定义的函数, 就像在下面的示例中在inner()内部定义了inner()一样。嵌套函数可以访问封闭范围的变量, 但不能修改它们, 除非你使用的是非本地变量。在第一个示例中, 你将看到number = 3赋值仅在inner()中成立。
def outer(number):
def inner():
number = 3
print("Inner: " + str(number))
inner()
print("Outer: " + str(number))
outer(9)
Inner: 3
Outer: 9
但是, 如果在inner()中添加非本地数字, 则external()的包围范围变量将由inner()更改。 number = 3现在适用于inner()和external()中的数字。
def outer(number):
def inner():
nonlocal number
number = 3
print("Inner: " + str(number))
inner()
print("Outer: " + str(number))
outer(9)
Inner: 3
Outer: 3
你可能需要保留在嵌套函数中定义的变量, 而不必更改全局变量。
在Python中, 函数也被视为对象, 这意味着可以将其返回并分配给变量。在下一个示例中, 你将看到, 使用return inner代替在inner()内部调用inner()。然后, 使用字符串参数调用external()并将其分配给闭包。现在, 即使函数inner()和external()已经完成执行, 它们的消息仍然保留。通过调用closure(), 可以打印消息。
def outer(message):
# enclosing function
def inner():
# nested function
print(message)
return inner
closure = outer("Hello world!")
closure()
Hello world!
请注意, 如果调用不带括号的闭包, 将仅返回对象的类型。你可以看到它的类型为__main __。outer。<locals> .inner。
closure
<function __main__.outer.<locals>.inner>
LEGB规则
如你之前所见, 名称空间可以彼此独立存在, 并且具有一定的层次结构级别, 我们称其为范围。根据你在程序中的位置, 将使用其他名称空间。要确定Python应该以什么顺序访问名称空间, 可以使用LEGB规则。
LEGB代表:
- 本地
- 封闭的
- 全球
- 内建
假设你要在inner()中调用print(x), 这是嵌套在external()中的函数。然后, Python将首先查看x是否在该inner()中本地定义。如果不是, 将使用external()中定义的变量。这是封闭功能。如果还没有在此定义, Python解释器将上升到全局范围的另一层。在此之上, 你只会找到内置范围, 该范围包含Python本身的特殊变量reserverd。
# Global scope
x = 0
def outer():
# Enclosed scope
x = 1
def inner():
# Local scope
x = 2
总结
太棒了!现在, 你知道Python的变量范围是什么, 应该如何使用global和nonlocal关键字以及LEGB规则。你将可以轻松操作嵌套函数中的变量, 而不会出现任何问题。这对于你将来作为数据科学家的职业将非常有用。如果你想进一步提高Python技能, 请务必查看我们的Python数据科学入门课程。它是免费的, 并且涵盖了Python编程语言的所有基础知识。
评论前必须登录!
注册