小蝌蚪找妈妈:Python之作用域链与 LEGB 原则

参考

项目描述
Python 官方文档builtins.html
搜索引擎GoogleBing

描述

项目描述
PyCharm2023.1 (Professional Edition)
Python3.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() 等类型转换函数。
  • ExceptionTypeErrorValueError 等异常类。
  • 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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryMoon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值