Python 3.12 Special Attribute - 25 - __cached__

张开发
2026/4/20 10:00:19 15 分钟阅读
Python 3.12 Special Attribute - 25 - __cached__
Python 3.12 Special Attribute -__cached____cached__是 Python 中模块module的一个内置特殊属性它指向该模块的字节码缓存文件.pyc文件的路径。当 Python 导入一个.py源文件时解释器会将其编译为字节码并缓存到__pycache__目录中除非使用-B选项或环境变量禁止。__cached__属性记录了该缓存文件的完整路径。理解__cached__有助于掌握 Python 的字节码缓存机制、优化导入性能、调试模块加载问题。1.__cached__的基本概念定义__cached__是一个字符串表示模块对应的.pyc文件字节码缓存的绝对路径。对于从.py源文件导入的模块该属性被自动设置对于内置模块如sys或动态创建的模块该属性可能为None或不存在。设置时机当模块通过import语句或importlib从.py源文件加载时Python 的加载器通常是SourceFileLoader会计算.pyc文件的路径通常位于__pycache__目录下并在模块对象创建后将其赋值给__cached__属性。可写性__cached__是可写的因为它是模块字典中的一项但通常只读。修改它不会影响实际的缓存行为但可能误导工具。示例importosprint(os.__cached__)# AttributeError: module os has no attribute __cached__print(os.__spec__.cached)# None2. 字节码缓存机制简介Python 是解释型语言源代码在执行前会被编译为字节码.pyc文件。为了提高启动速度解释器会检查源文件.py的修改时间并与.pyc文件的元数据比较。如果.pyc文件存在且未过期则直接加载字节码跳过重新编译。__pycache__目录是 Python 3.2 引入的标准化缓存目录命名格式为module.cpython-版本号.pyc。缓存路径生成规则源文件路径/path/to/module.py缓存目录/path/to/__pycache__/缓存文件名module.cpython-312.pyc其中312为 Python 版本号__cached__值为/path/to/__pycache__/module.cpython-312.pyc3. 不同模块的__cached__行为模块类型__cached__值示例从.py文件导入的普通模块指向.pyc文件的绝对路径/usr/lib/python3.12/__pycache__/os.cpython-312.pyc内置模块如sys,math通常为None没有源文件None从.pyc文件直接导入无源文件指向该.pyc文件路径例如import module且只有.pyc文件时__cached__等于__file__命名空间包无__init__.py无此属性因为不是文件模块AttributeError动态创建的模块types.ModuleType未设置通常为NoneNone从 ZIP 或归档加载的模块可能指向存档内的路径如some.zip/module.pyc特殊字符串4. 用途与典型场景检查模块是否有缓存通过hasattr(module, __cached__)和module.__cached__ is not None判断模块是否基于文件缓存。获取缓存文件路径用于调试、清除缓存、检查版本等。与__file__配合__file__指向源文件或.pyc文件如果源文件缺失__cached__明确指向.pyc文件。在导入钩子中使用自定义加载器可以设置__cached__以模拟标准行为。性能分析了解哪些模块被缓存哪些没有被缓存有助于优化启动时间。5. 示例与逐行解析示例 1查看普通模块的__cached__importosimportsysimportjson# print(fos.__cached__: {os.__cached__}) # AttributeError: module os has no attribute __cached__# print(fsys.__cached__: {sys.__cached__}) # AttributeError: module sys has no attribute __cached__print(fjson.__cached__:{json.__cached__})# /usr/local/lib/python3.12/json/__pycache__/__init__.cpython-312.pyc输出路径可能因系统而异json.__cached__: /usr/lib/python3.12/__pycache__/json.cpython-312.pyc逐行解析行代码解释1-3导入三个模块json是从文件加载的sys是内置模块。5-7打印__cached__json有.pyc文件路径为什么这样写通过对比可以看出内置模块没有对应的.pyc文件而文件模块通常有缓存。示例 2检查__cached__与__file__的关系importosprint(f__file__:{os.__file__})# print(f__cached__: {os.__cached__}) # AttributeError: module os has no attribute __cached__输出__file__: /usr/lib/python3.12/os.py逐行解析__file__指向源文件.py__cached__指向对应的字节码缓存文件。示例 3手动清除模块的__cached__不推荐importjsonprint(json.__cached__)# 原始路径json.__cached__None# 手动修改print(json.__cached__)# None注意修改__cached__不会影响实际缓存但可能导致工具误判。示例 4使用importlib.util.find_spec获取模块的缓存路径importimportlib.util# 应该在windows、linux、mac不同系统平台上做验证。specimportlib.util.find_spec(os)ifspec:print(fspec.origin:{spec.origin})# 源文件路径print(fspec.cached:{spec.cached})# 缓存文件路径对应 __cached__输出spec.origin: frozen spec.cached: None逐行解析importlib.util.find_spec返回ModuleSpec对象其中cached字段就是__cached__的值。这是更现代的自省方式。示例 5动态创建模块并手动设置__cached__importtypesimportimportlib.machinery moduletypes.ModuleType(my_module)module.__file__/path/to/my_module.pymodule.__cached__/path/to/__pycache__/my_module.cpython-312.pyc# 模拟导入后的状态解析虽然通常不手动设置但在自定义加载器中可能需要。6. 底层实现机制CPython在 CPython 中__cached__的设置与SourceFileLoader紧密相关。SourceFileLoader的get_code方法当从.py文件加载模块时SourceFileLoader会调用_get_code来获取代码对象。它会计算源文件的修改时间并检查是否存在有效的.pyc文件。路径计算SourceFileLoader使用_cache_from_source函数位于importlib/_bootstrap_external.py来生成.pyc文件路径。规则为在源文件同级目录下创建__pycache__子目录文件名格式为{name}.cpython-{version}.pyc。赋值在模块成功加载后SourceFileLoader.exec_module会设置module.__cached__为该路径。如果模块是从.pyc直接加载无源文件则__cached__等于__file__。内置模块BuiltinImporter不会设置__cached__因此为None。性能优化__cached__的存在允许 Python 快速定位缓存文件避免重复编译。但访问该属性本身几乎没有开销。7. 注意事项与陷阱__cached__可能为None对于内置模块、动态模块、命名空间包等__cached__可能不存在或为None。访问前应使用hasattr(module, __cached__)检查。__cached__与__file__的区别__file__指向源文件.py或直接加载的.pyc文件如果没有源文件。__cached__明确指向.pyc文件通常位于__pycache__中。对于直接从.pyc加载的模块__file__和__cached__可能相同。可写性虽然可以修改__cached__但不建议因为不会影响实际导入行为且可能破坏依赖此属性的工具。与importlib.invalidate_caches()的关系清除缓存不会影响已加载模块的__cached__属性但会刷新查找器缓存。__pycache__目录如果源文件所在目录不可写Python 可能不会生成.pyc文件此时__cached__可能为None或者指向一个不存在的路径实际上SourceFileLoader在无法写入缓存时会继续使用源文件但__cached__仍会被设置为预期的缓存路径尽管文件可能不存在。8. 与其他特殊属性的关系属性关系__file__源文件路径或直接加载的.pyc路径。__spec__.cached与__cached__相同是ModuleSpec中的对应字段。__loader__加载器对象__cached__通常由SourceFileLoader设置。__name__模块名。__path__包的搜索路径与__cached__无关。9. 总结特性说明角色存储模块的字节码缓存文件.pyc的路径类型str或None适用对象从.py源文件导入的模块以及某些从.pyc直接导入的模块访问方式module.__cached__可写性可写但不推荐修改底层由SourceFileLoader在加载模块时根据__pycache__规则计算并设置典型用途自省、调试、获取字节码文件路径、优化启动性能最佳实践优先使用importlib.util.find_spec获取spec.cached不要手动修改注意可能为None掌握__cached__有助于理解 Python 的字节码缓存机制以及模块导入系统的内部工作。通过合理使用可以更好地优化程序启动速度和调试模块加载问题。希望本文能帮助你全面掌握这一特殊属性。如果在学习过程中遇到问题欢迎在评论区留言讨论!

更多文章