在 Python 中,模块(module)是一级命名空间对象。无论是通过 import 加载的标准库模块、第三方模块,还是直接运行的脚本本身,每个模块都以对象形式存在于内存中,并通过其 __dict__ 属性统一管理所有模块级名称。
理解模块的 __dict__,不仅是掌握模块工作机制的关键,也是深入理解 Python 命名空间模型、导入机制以及运行期动态行为的重要基础。
一、模块对象与模块命名空间
(1)模块是运行期对象,而非静态概念
在 Python 中,模块不是“代码文件”的抽象概念,而是运行期存在的实际对象。
print(type(math)) #每一个模块对象都拥有一组标准属性,例如:
• __name__:模块名称
• __file__:模块源文件路径
• __spec__:模块的导入规范
• __loader__:模块加载器
• __dict__:模块命名空间
其中,__dict__ 是模块命名空间的唯一物理载体,存储了模块中的所有名称。
(2)模块 __dict__ 的定义
模块 __dict__ 表示模块级的命名空间本身。模块中通过赋值语句、函数定义、类定义、import 语句创建的所有名称,最终都存储在这个字典中。
示例:
pass加载后查看其命名空间:
print(demo.__dict__.keys())输出:
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'x', 'f', 'C'])可以看到,模块 __dict__ 既包含用户定义的变量、函数、类,也包含系统自动添加的元信息。
二、types 模块与 ModuleType 的作用
为了准确理解模块 __dict__,需要明确模块对象的具体类型。
(1)types 模块的角色
标准库 模块提供了对解释器内部核心对象类型的标准化引用,如:
• FunctionType 函数类型
• MethodType 方法类型
• ModuleType 模块类型
• GeneratorType 生成器类型
这些不是“新类型”,而是对既有内置类型的正式命名,便于类型检查和明确意图。
(2)ModuleType 的本质
types.ModuleType 表示模块对象的类型:
print(type(math) is types.ModuleType) # True所有通过 import 得到的模块对象,本质上都是 ModuleType 的实例。
(3)ModuleType 的构造语义
types.ModuleType(name, doc=None)参数含义:
• name:模块名(对应 __name__)
• doc: 模块文档字符串(对应 __doc__)
示例:
print(m.__dict__) # {}(4)ModuleType 创建的是真实模块对象
需要明确的一点是,types.ModuleType 创建的并不是“伪模块”,而是真正的模块对象。区别仅在于:
特性
import 创建的模块
ModuleType 创建的模块
代码执行
自动执行模块代码
不自动执行代码
__dict__ 填充
自动填充
初始为空字典
系统属性
自动设置 __spec__、__loader__
需手动设置
注册到 sys.modules
自动注册
不自动注册
在对象模型层面,它们完全一致:
print(type(m1) is type(m2)) # True(5)使用 ModuleType 的意义
使用 ModuleType 的目的在于:隔离模块对象结构本身与 import 机制的副作用。
示例:
print(m.__dict__) # {'x': 42, 'hello': at ...>}这清晰地体现了:
• 模块属性 ≡ 模块 .__dict__ 中的键值对
• 模块命名空间完全由模块 __dict__ 管理
三、模块 __dict__ 的生命周期
模块 __dict__ 的生命周期与模块对象本身严格一致。
(1)创建阶段
执行顺序为:
1、创建模块对象:实例化 ModuleType
2、初始化空 __dict__:创建空字典作为命名空间
3、执行模块代码:逐条执行语句,将结果写入 __dict__。
exec(open('demo.py').read(), demo.__dict__) # 导入过程的“概念性等价描述”(2)运行阶段:动态可变
模块 __dict__ 是一个完全可写的普通字典,可在运行时动态改变:
del demo.__dict__['new_var']模块在运行期可以:
• 动态添加属性/删除属性
• 可被反射、注入、修改
• 作为插件系统的载体
(3)销毁阶段
当模块对象被垃圾回收时,模块 __dict__ 随对象一同销毁,其命名空间不再存在。
通常,模块会常驻内存,因为 sys.modules 持有对模块对象的强引用。除非显式从 sys.modules 中删除,否则模块对象及其 __dict__ 会一直存在。
四、模块 __dict__ 与属性访问机制
模块属性访问遵循简单直接的规则:
module.attr在属性存在的情况下,在语义上等价于:
module.__dict__['attr']二者在异常类型上略有差异。
属性访问特点:
• 无方法绑定:模块中的函数始终是普通函数对象。
• 无 MRO 查找:不存在继承链查找。
• 无描述符处理:不涉及 __get__/__set__ 协议,即使模块中存在实现了描述符协议的对象,模块属性访问也不会触发描述符绑定逻辑。
• 直接字典查找:属性访问就是字典键查找。
示例:
demo.f() # 返回 "function",不会生成方法对象五、模块 __dict__ 的典型用途
(1)反射与调试
可用于枚举模块成员、动态分析模块结构以及 REPL / 调试器实现。
pdb.set_trace() # 调试器内部使用 __dict__ 访问局部变量(2)动态注入 API
常见于插件系统、框架自动注册以及运行期扩展接口。
sys.modules[module_name] = module(3)框架自动注册
return decorator六、不同对象的命名空间对比
对象类型
__dict__ 特性
可变性
特殊行为
实例
普通 dict
完全可变
支持属性访问协议
mappingproxy
需通过赋值语句
支持继承、描述符
模块
普通 dict(完全可写)
完全可变
直接字典映射
模块是唯一一个顶层、全局、完全可变的命名空间对象,这也是它常被用作配置容器、插件载体、全局状态管理的原因。
小结
模块 __dict__ 是模块命名空间的实体载体,保存了模块中定义的全部名称。模块对象本质上是 types.ModuleType 的实例,其 __dict__ 是一个普通、完全可写的字典。模块加载时,解释器创建模块对象并逐步填充其 __dict__;运行期中,该字典可被动态修改。模块属性访问直接映射到 __dict__ 查找,不涉及方法绑定或 MRO。
理解模块 __dict__,是理解 Python 命名空间、import 机制以及动态特性的关键基础。
“点赞有美意,赞赏是鼓励”
热门跟贴