文章目录
- 参考
- 描述
- 作用域对象
- 全局作用域
- globals()
- 局部作用域
- locals()
- 包含作用域
- 内置作用域
- builtins 模块
- builtins 模块与 \_\_builtins__
- builtins is \_\_builtins__???
- \_\_builtins__ 与内置作用域
- 赶不走的 \_\_builtins__
- 作用域链
- 作用域链 与 LEGB 原则
- 狗急跳墙之法
参考
项目 | 描述 |
---|---|
Python 官方文档 | builtins.html |
搜索引擎 | Google 、Bing |
描述
项目 | 描述 |
---|---|
PyCharm | 2023.1 (Professional Edition) |
Python | 3.10.6 |
作用域对象
作用域对象(Scope Object)是一个在 Python 中 用于管理命名空间的内部数据结构
,它是 Python 解释器在执行代码期间维护和使用的重要组成部分。
每当创建一个新的函数、类或模块时,Python 解释器都会创建一个相应的作用域对象来跟踪该作用域内的变量、函数和其他命名项。作用域对象形成了一个层次结构,这样有利于按照特定的规则进行变量和命名空间的查找和访问。
作用域对象主要起如下作用:
项目 | 描述 |
---|---|
命名空间 | 作用域对象维护一个命名空间,用于存储在该作用域中定义的变量、函数和其他命名项。命名空间 可以看作是一个 字典 ,其中 键 是名称,值 是与名称关联的对象。 |
变量查找 | 当在作用域中引用一个变量时,Python 解释器会按照 特定的规则 在作用域对象的命名空间中查找该变量。 |
命名冲突解决 | 作用域对象帮助解决同一作用域中可能存在的命名冲突问题,确保每个名称在其命名空间中是唯一的。当在作用域中定义了 多个同名的变量或函数 时,作用域对象确保每个名称在其命名空间中是 唯一的 ,以避免混淆和错误。 |
作用域链 | 作用域对象通过构建一个 嵌套的链表结构 来实现变量和命名空间的查找。该链表按照 特定的顺序 链接了所有嵌套的作用域对象,使得解释器能够按照规定的顺序在不同的作用域中进行查找。 |
作用域切换 | 当程序执行流进入或离开一个作用域时,作用域对象负责管理当前活动作用域的状态。作用域对象将在程序执行流进入作用域时被创建并在其离开作用域时进行清理,以确保 正确的命名空间的访问 和 生命周期管理 。 |
全局作用域
全局作用域(Global Scope)是指在整个程序中可见的、位于 模块级别
的作用域。它是在模块中定义的变量、函数和类的作用域范围。
在Python中,全局作用域(Global Scope)指的是在 整个程序中
可见的、位于 模块级别
的作用域。全局作用域包含了在 全局范围内
定义的变量、函数、类和其他命名项。全局作用域是程序启动时创建的,一直存在
于整个程序的执行过程中。
全局作用域与 Python 中的 模块
密切相关。每个 Python 文件
都可以看作是一个模块,模块具有自己的全局作用域。在模块中定义的全局变量和函数可以在整个模块中访问和使用。
globals()
globals()
是一个内置函数,它返回一个 表示当前模块全局命名空间的字典
,该字典存储了当前模块中定义的所有全局变量和函数。
举个栗子
free_var = 'Hello World'
# globals() 在程序的任何位置访问的结果都是
# 一致的。
for name in globals().copy():
print(f'{name}\t{globals()[name]}')
# 在调用 globals() 函数时,MyClass
# 与 func 都尚未添加到全局命名空间中。
# 仅但解释器执行到 MyClass 与 func 的定义部分
# Python 才会将其添加至全局命名空间。
class MyClass:
pass
print()
print('MyClass' in globals())
print('func' in globals())
print()
def func():
pass
print('MyClass' in globals())
print('func' in globals())
执行效果
当解释器加载一个 模块(任何 Python 文件都可被视为模块)
时,它首先会创建一个新的全局作用域对象,用于存储该模块的全局变量、函数、类和其他定义。解释器会 按顺序解析
模块中的代码,并在执行过程中 将标识符绑定到相应的作用域对象中
。
__name__ __main__
__doc__ None
__package__ None
__loader__ <_frozen_importlib_external.SourceFileLoader object at 0x000002868F414820>
__spec__ None
__annotations__ {}
__builtins__ <module 'builtins' (built-in)>
__file__ C:\Users\RedHeart\PycharmProjects\pythonProject\example.py
__cached__ None
free_var Hello World
True
False
True
True
局部作用域
在 Python
中,每当函数被调用时,都会为其创建一个新的局部作用域对象。这意味着 每个函数
都有自己的 独立的
局部作用域,其中定义的变量只在函数执行期间存在,当 函数返回时
,局部作用域及其变量都会被 销毁
。
局部作用域提供了一种封装变量的机制,使得它们不会与其他函数或全局作用域中的变量发生冲突。这有助于提高代码的可读性、可维护性和重用性。
locals()
在 Python 中,locals()
是一个内置函数,用于返回 当前作用域
中的局部变量和其对应的值的字典,其中键是变量名,值是对应变量的值。对此,请参考如下示例:
# 当在全局作用域中调用 locals() 函数时,
# 该函数将返回全局命名空间字典,与 globals()
# 返回的值相同。
print(locals() == globals())
def func():
# 当在函数中调用 locals() 函数时,
# 该函数将返回其所在函数的局部命名空间字典。
local_var = 'Hello World'
print(locals())
func()
执行效果
True
{'local_var': 'Hello World'}
包含作用域
当在一个局部作用域内部定义了另一个作用域(例如,在函数内部定义了另一个函数),就产生了包含作用域。在相互嵌套的函数级作用域中,最内层的作用域被称为局部作用域,其他作用域被称为包含作用域。
举个栗子
# 全局作用域
def external_func():
# 包含作用域
def middle_func():
# 包含作用域
def internal_func():
# 局部作用域
pass
内置作用域
在 Python 中,内置作用域(Built-in Scope)指的是 Python 解释器预先定义的一组函数和变量,它们可以在任何地方直接使用,无需导入其他模块。
内置作用域中包含了一些常用的对象(如 print()
、len()
、range()
、True
等),用于执行各种常见的任务和操作。这些对象可以被 所有
的 Python 代码 直接访问
,而 无需显式地导入任何模块
。
builtins 模块
在Python中,builtins
模块是一个内置模块,它包含了 Python 的内置函数、异常和其他一些内置对象。这个模块将在 Python 解释器启动时自动加载,因此你可以直接访问其中定义的对象,而无需显式导入。
builtins
模块提供了许多常用的内置函数和内置对象,例如:
print()
:用于输出文本到控制台。input()
:用于从控制台读取用户输入。len()
:用于获取对象的长度。int()
、float()
、str()
等类型转换函数。Exception
、TypeError
、ValueError
等异常类。abs()
、round()
、min()
、max()
等常用函数。
builtins 模块与 __builtins__
在 Python 中,__builtins__
是一个特殊的全局变量,它指向一个字典,其中包含了内置函数、异常和对象的名称空间。在模块的全局作用域中,可以通过 __builtins__
来访问这些内置函数和对象。
当使用 import builtins
导入 builtins
模块时,builtins
模块本身会被加载并创建一个模块对象。然后,模块对象的 __dict__
(__dict__
属性是一个字典,用于存储对象的实例变量和方法)属性会被用于初始化 __builtins__
变量,使其指向 builtins
模块的名称空间。
因此,可以说 __builtins__
是 builtins
模块导入至模块中的结果。通过__builtins__
,可以在模块中访问到 builtins
模块提供的内置函数和对象,例如 __builtins__.print()
、__builtins__.len()
等。
builtins is __builtins__???
在 Python 中,多次导入同一模块的情况下,实际上只会执行一次模块的加载和初始化过程。之后的导入操作会直接引用已经加载的模块对象,而不会再次执行模块内的代码。
这意味着,多次导入同一模块并不会导致模块内的代码被重复执行。只有在第一次导入时,模块内的代码才会被执行一次,并且模块对象会被创建。之后的导入操作将简单地引用已经加载的模块对象。
Python 解释器会在运行 Python 文件时自动导入 builtins
模块,显式导入 builtins
模块将直接引用已加载的 builtins
模块对象。因此,builtins is __builtins__
的结果将为 True
。
__builtins__ 与内置作用域
在 Python 中,内置作用域对象可以通过访问全局变量 __builtins__
来进行访问。__builtins__
是一个特殊的全局变量,它指向一个模块对象,该模块对象包含了 Python 的内置对象。
赶不走的 __builtins__
在 Python 中,导入的模块可以通过 __del__
关键字进行删除。对此,请参考如下示例:
# 判断 sys 模块是否已经被导入
print('sys' in globals())
# 导入 sys 模块
import sys
print('sys' in globals())
# 尝试使用 del 关键字删除已导入模块 sys
del sys
print('sys' in globals())
执行效果
False
True
False
但对于 Python 自动导入至全局作用域的 builtins
对象来说,你是无法通过任何方式将其删除的。这是因为,builtins
是 解释器的一部分
,它是在解释器启动时创建的,并且在整个解释器的生命周期中保持不变。因此,我们不能通过常规的方法来删除或修改 builtins
。
你可以删除或修改 Python 提供的内置作用域对象的全局变量形式 __builtins__
,但这并不会导致 Python 解释器无法使用 builtins
模块提供的基础部件。对此,请参考如下示例:
print('__builtins__' in globals())
# 尝试使用 del 关键字删除 __builtins__ 变量
del __builtins__
# 仍然可以使用 print()、globals() 及 in 等内置对象
print('__builtins__' in globals())
作用域链
作用域链 与 LEGB 原则
LEGB
原则作用域链(Scope chain)和是密切相关的概念,用于描述变量查找和访问的顺序。
LEGB
是指 Python 中对象查找的四个层次,分别是局部作用域、包含作用域、全局作用域以及内置作用域。
作用域链是描述变量查找的顺序,它根据 LEGB 原则形成
。当在代码中引用一个对象时,Python 会 按照 LEGB 的顺序进行对象的查找
,直到找到 第一个匹配
的变量。
下面是一个示例,展示了作用域链和 LEGB 原则的应用:
# 内置作用域中定义的变量
__builtins__.k = 40
# 全局作用域中定义的变量
x = 10
def outer():
# 包含作用域中定义的变量
y = 20
def inner():
# 局部作用域中定义的变量
z = 30
# 由于在局部作用域中查找到了变量 y,
# 故不再沿着作用域链查找 y 的值,故 y = 30。
y = 30
# 10 + 30 + 30 + 40 = 110
print(x + y + z + k)
inner()
outer()
执行效果
110
狗急跳墙之法
由 LEGB 原则
可知,当除内置作用域外的其他作用域中若存在与内置对象同名的对象时,内置对象将被其他同名对象所 遮盖
。
在 Python 中,当存在同名的对象覆盖了 Python 提供的内置对象时,可以使用 __builtins__
来引用原始的内置对象。对此,请参考如下示例:
def print(content):
pass
# 内置对象 print() 已被全局作用域中的同名函数 print() 所遮盖
print('Hello World')
# 通过使用 __builtins__ 全局变量访问直接访问内置对象
__builtins__.print('Hello China')
执行效果
Hello China