这篇文章翻译自https://www.pythontutorial.net/advanced-python/python-variable-scopes/
Python 变量作用域
摘要:在这篇教程,你将学习 Python 作用域的工作原理。看完这篇教程,你将对内置,局部和全局变量有一个很好的理解。
变量作用域的介绍
当你把一个对象分配给一个变量时,该变量将在内存中引用该对象。这就是说,该变量被绑定到该对象。
赋值后,你可以在代码的许多部分使用该变量名来访问该对象。但是,你不能在所有代码中访问该变量。
变量名称和它的绑定(名称和对象)只存在于你代码的特定部分。
你定义名称/绑定的代码部分被称为变量的词法范围。
Python 将这些绑定存储在称为命名空间的东西中。每个作用域都有自己的命名空间。
你可以认为命名空间是一个表,它包含了标签和标签所绑定的引用。
全局作用域
全局作用域基本上就是模块作用域。全局作用域只跨越一个 Python 源代码文件。
除了内置作用域,Python 没有一个真正的全局作用域可以跨越所有的模块。
内置作用域是一个特殊的作用域,它提供全局可用的对象,如 print , len , None , True , 和 False 。
基本上,内置变量和全局变量存在于一个模块内部的任何地方。
在内部,全局作用域被嵌套在内置作用域中。
如果你从一个作用域访问一个变量,而 Python 在该作用域的命名空间中找不到它,它将在包围的作用域的命名空间中搜索。
假设你在一个名为 app.py 的模块中拥有以下语句。
print('Hello')
在这个 app.py 模块中,Python 在模块范围内寻找 print 函数 ( app.py )。
由于 Python 在 app.py 模块作用域中没有找到 print 函数的定义,Python 上升到包围作用域,也就是内置作用域,并在那里寻找 print 函数。在这种情况下,它可以在内置作用域中找到 print 函数。
如果你把语句改为以下内容,你会得到一个运行时错误。
print(counter)
在这个例子中,Python 在当前全局作用域中没有找到 counter 。因此,Python 在包围的作用域(内置作用域)中寻找它。
然而,变量 counter 并不存在于内置的作用域中。因此,Python 发布了一个 NameError 异常。
NameError: name 'counter' is not defined
局部作用域
当创建一个函数时,你可以为该函数定义参数和变量。例如:
def increment(counter, by=1):
result = counter + by
return result
当你执行代码时,Python带有两个阶段:编译和执行。(译者注:这里的编译阶段指的是将代码转换为字节码的过程,执行阶段指的是将字节码转换为机器码的过程。)
当 Python 编译文件时,它将 increment 函数添加到全局范围。此外,Python 决定 increment() 函数内的 counter 、 by 和 result 变量是 increment() 函数的局部变量。而且在函数执行之前,Python 不会创建 counter 、 by 和 result 变量。
每当你调用一个函数,Python 都会创建一个新的作用域。Python 也会把在函数中定义的变量分配给这个作用域。而这个作用域被称为函数局部作用域或局部作用域。
在我们的例子中,当你调用 increment() 函数时。
increment(10, 2)
…Python 为 increment() 函数的调用创建了一个局部作用域。
另外,Python在局部命名空间中创建了局部变量 counter 、 by 和 result ,并将它们与值 10 、 2 和 12 变量绑定。
当函数完成时,Python 将删除局部作用域。而所有的局部变量,如 counter 、 by 和 result 变量都超出了范围。(译者注:此处指函数执行完成时,局部变量被清除,并释放空间)如果你试图从 increment() 函数之外访问这些变量,你会得到一个错误。
而如果你再次调用 increment() 函数。
increment(100, 3)
Python创建了一个新的局部作用域和变量,包括 counter 、 by 和 result 并将它们与值 100 、 3 和 103 绑定。
变量查找顺序
在 Python 中,作用域是嵌套的。例如,局部作用域被嵌套在模块作用域中。而模块作用域是嵌套在内置作用域里面的。
当你访问一个与变量绑定的对象时,Python 会试图找到这个对象。
-
首先在当前的局部作用域内查找。
-
如果 Python 在当前作用域中没有找到该对象,就会沿着包围作用域的链条向上走。
全局关键字
当你从一个函数内部检索一个全局变量的值时,Python 会自动搜索本地作用域的名字空间,并向上搜索所有包围作用域的命名空间链。比如说:
counter = 10
def current():
print(counter)
current()
在这个例子中,当 current() 函数运行时,Python 在局部作用域内寻找 counter 变量。
由于 Python 没有找到它,它就在全局作用域内搜索这个变量。而在这种情况下,Python可以找到 counter 变量。
然而,如果你从一个函数中给全局变量赋值,Python 将把这个变量放到局部命名空间中。比如说
counter = 10
def rset():
counter = 0
print(counter)
reset()
print(counter)
输出:
0
10
在编译时,Python 将 counter 解释为一个局部变量。
当 reset() 函数运行时,Python在局部范围内找到 counter 。在 reset() 函数内部的 print(counter) 语句显示了 counter 的值,它是0。
当我们在 reset() 函数完成后打印 counter 时,显示的却是10。
在这个例子中,局部 counter 变量掩盖了全局 counter 变量。
如果你想从一个函数内部访问一个全局变量,你可以使用 global 关键字。比如说
counter = 10
def reset():
global counter
counter = 0
print(counter) # 0
reset()
print(counter) # 0
输出:
0
0
在这个例子中,下面的语句。
global counter
指示 Python 将 counter 变量绑定到全局作用域,而不是局部作用域。
请注意,在一个函数内访问全局变量不是一个好的习惯。
总结
-
变量的作用域是你可以访问该变量的代码部分
-
内置作用域的变量可以从任何地方访问。
-
全局作用域(或模块作用域)可以从模块的每个部分访问。
-
局部作用域可以从一个函数内部访问。
-
Python 在作用域的命名空间中存储对象和它们的绑定关系。
-
Python 首先在当前作用域中查找一个对象,如果 Python 没有找到它,就上升到包围的作用域。
-
Python的作用域是嵌套的。
-
使用 global 关键字从一个函数内部访问一个全局变量。
(全文完)
部分图片来自于源网站,侵删。
鉴于本人才疏学浅,翻译难免有所疏漏,如果有任何问题欢迎随时联系我进行批评指正:2076577077@qq.com
我是gled fish, 点击这里来到我的博客网站:
转载请注明译者和原出处,请勿用于任何商业用途。