Python:模块 __dict__ 详解

在 Python 中,模块(module)是一级命名空间对象。无论是通过 import 加载的标准库模块、第三方模块,还是直接运行的脚本本身,每个模块都以对象形式存在于内存中,并通过其 dict 属性统一管理所有模块级名称。

理解模块的 dict,不仅是掌握模块工作机制的关键,也是深入理解 Python 命名空间模型、导入机制以及运行期动态行为的重要基础。

一、模块对象与模块命名空间

(1)模块是运行期对象,而非静态概念

在 Python 中,模块不是"代码文件"的抽象概念,而是运行期存在的实际对象。

python 复制代码
import math
print(type(math))    # <class 'module'>

每一个模块对象都拥有一组标准属性,例如:

name:模块名称

file:模块源文件路径

spec:模块的导入规范

loader:模块加载器

dict:模块命名空间

其中,dict 是模块命名空间的唯一物理载体,存储了模块中的所有名称。

(2)模块 dict 的定义

模块 dict 表示模块级的命名空间本身。模块中通过赋值语句、函数定义、类定义、import 语句创建的所有名称,最终都存储在这个字典中。

示例:

python 复制代码
# demo.py"""示例模块"""x = 10
def f():    """示例函数"""    pass
class C:    """示例类"""    pass

加载后查看其命名空间:

java 复制代码
import demo
print(demo.__dict__.keys())

输出:

css 复制代码
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'x', 'f', 'C'])

可以看到,模块 dict 既包含用户定义的变量、函数、类,也包含系统自动添加的元信息。

二、types 模块与 ModuleType 的作用

为了准确理解模块 dict,需要明确模块对象的具体类型。

(1)types 模块的角色

标准库 types 模块提供了对解释器内部核心对象类型的标准化引用,如:

• FunctionType 函数类型

• MethodType 方法类型

• ModuleType 模块类型

• GeneratorType 生成器类型

这些不是"新类型",而是对既有内置类型的正式命名,便于类型检查和明确意图。

(2)ModuleType 的本质

types.ModuleType 表示模块对象的类型:

python 复制代码
import math, types
print(type(math) is types.ModuleType)    # True

所有通过 import 得到的模块对象,本质上都是 ModuleType 的实例。

(3)ModuleType 的构造语义

python 复制代码
types.ModuleType(name, doc=None)

参数含义:

• name:模块名(对应 name

• doc: 模块文档字符串(对应 doc

示例:

python 复制代码
import types
m = types.ModuleType('demo', '示例模块')print(m.__name__)   # 'demo'print(m.__doc__)    # '示例模块'print(m.__dict__)   # {}

(4)ModuleType 创建的是真实模块对象

需要明确的一点是,types.ModuleType 创建的并不是"伪模块",而是真正的模块对象。区别仅在于:

|-----------------|--------------------------|------------------|
| 特性 | import 创建的模块 | ModuleType 创建的模块 |
| 代码执行 | 自动执行模块代码 | 不自动执行代码 |
| dict 填充 | 自动填充 | 初始为空字典 |
| 系统属性 | 自动设置 specloader | 需手动设置 |
| 注册到 sys.modules | 自动注册 | 不自动注册 |

在对象模型层面,它们完全一致:

python 复制代码
import types
m1 = types.ModuleType('demo')m2 = __import__('demo')  # 通过导入创建
print(type(m1) is type(m2))  # True

(5)使用 ModuleType 的意义

使用 ModuleType 的目的在于:隔离模块对象结构本身与 import 机制的副作用。

示例:

python 复制代码
import types
# 创建一个干净的模块对象m = types.ModuleType('demo')m.x = 42m.hello = lambda: "Hello"
print(m.__dict__)  # {'x': 42, 'hello': <function <lambda> at ...>}

这清晰地体现了:

• 模块属性 ≡ 模块 .dict 中的键值对

• 模块命名空间完全由模块 dict 管理

三、模块 dict 的生命周期

模块 dict 的生命周期与模块对象本身严格一致。

(1)创建阶段

执行顺序为:

1、创建模块对象:实例化 ModuleType

2、初始化空 dict:创建空字典作为命名空间

3、执行模块代码:逐条执行语句,将结果写入 dict

python 复制代码
# 导入 demo 模块的等价过程(概念上)import typesimport sys
# 1. 创建模块对象demo = types.ModuleType('demo')
# 2. 初始属性demo.__file__ = 'demo.py'demo.__loader__ = ...
# 3. 执行代码,填充 __dict__exec(open('demo.py').read(), demo.__dict__)    # 导入过程的"概念性等价描述"

(2)运行阶段:动态可变

模块 dict 是一个完全可写的普通字典,可在运行时动态改变:

python 复制代码
import demo
# 两种修改方式等价demo.new_var = 100demo.__dict__['another_var'] = 200
# 动态添加函数demo.dynamic_func = lambda x: x * 2
# 删除属性del demo.xdel demo.__dict__['new_var']

模块在运行期可以:

• 动态添加属性/删除属性

• 可被反射、注入、修改

• 作为插件系统的载体

(3)销毁阶段

当模块对象被垃圾回收时,模块 dict 随对象一同销毁,其命名空间不再存在。

通常,模块会常驻内存,因为 sys.modules 持有对模块对象的强引用。除非显式从 sys.modules 中删除,否则模块对象及其 dict 会一直存在。

四、模块 dict 与属性访问机制

模块属性访问遵循简单直接的规则:

javascript 复制代码
module.attr

在属性存在的情况下,在语义上等价于:

javascript 复制代码
module.__dict__['attr']

二者在异常类型上略有差异。

属性访问特点:

• 无方法绑定:模块中的函数始终是普通函数对象。

• 无 MRO 查找:不存在继承链查找。

• 无描述符处理:不涉及 get/set 协议,即使模块中存在实现了描述符协议的对象,模块属性访问也不会触发描述符绑定逻辑。

• 直接字典查找:属性访问就是字典键查找。

示例:

python 复制代码
# demo.pydef f():    return "function"
# 调用时无隐式 selfimport demodemo.f()  # 返回 "function",不会生成方法对象

五、模块 dict 的典型用途

(1)反射与调试

可用于枚举模块成员、动态分析模块结构以及 REPL / 调试器实现。

python 复制代码
import demo
# 获取模块所有成员members = vars(demo)  # 等同于 demo.__dict__for name, value in members.items():    if not name.startswith('__'):        print(f"{name}: {type(value).__name__}")
# 调试器常用import pdbpdb.set_trace()  # 调试器内部使用 __dict__ 访问局部变量

(2)动态注入 API

常见于插件系统、框架自动注册以及运行期扩展接口。

python 复制代码
# 插件系统示例import types
def register_plugin(module_name, func):    """动态注册函数到模块"""    module = types.ModuleType(module_name)    module.handler = func    sys.modules[module_name] = module

(3)框架自动注册

python 复制代码
# Web 框架路由注册示例import sys
routes = {}
def route(path):    def decorator(func):        routes[path] = func        # 同时添加到模块命名空间        sys.modules[func.__module__].__dict__[func.__name__] = func        return func    return decorator

六、不同对象的命名空间对比

对象类型 __dict__ 特性 可变性 特殊行为
实例 普通 dict 完全可变 支持属性访问协议
mappingproxy 需通过赋值语句 支持继承、描述符
模块 普通 dict(完全可写) 完全可变 直接字典映射

模块是唯一一个顶层、全局、完全可变的命名空间对象,这也是它常被用作配置容器、插件载体、全局状态管理的原因。

📘 小结

模块 dict 是模块命名空间的实体载体,保存了模块中定义的全部名称。模块对象本质上是 types.ModuleType 的实例,其 dict 是一个普通、完全可写的字典。模块加载时,解释器创建模块对象并逐步填充其 dict;运行期中,该字典可被动态修改。模块属性访问直接映射到 dict 查找,不涉及方法绑定或 MRO。

理解模块 dict,是理解 Python 命名空间、import 机制以及动态特性的关键基础。

"点赞有美意,赞赏是鼓励"

相关推荐
jarreyer2 小时前
python,numpy,pandas和matplotlib版本对应关系
python·numpy·pandas
字节跳动开源2 小时前
Midscene v1.0 发布 - 视觉驱动,UI 自动化体验跃迁
前端·人工智能·客户端
山峰哥2 小时前
SQL调优核心战法——索引失效场景与Explain深度解析
大数据·汇编·数据库·sql·编辑器·深度优先
光影少年2 小时前
三维前端需要会哪些东西
前端·webgl
GottdesKrieges2 小时前
OMS迁移平台问题排查思路
数据库
代码or搬砖2 小时前
HashMap源码
开发语言·python·哈希算法
源力祁老师2 小时前
Odoo 客户端注册表
数据库
星辰_mya2 小时前
reids哨兵集群与选主
java·开发语言
学Linux的语莫3 小时前
Milvus向量数据库的操作(基于Langchain)
数据库·langchain·milvus