如何不依赖__file__获取Python文件/模块信息?可借助模块级__getattr__?
替代__file__实现Exporter实例化的方案
你可以通过Python的模块自省机制,完全避免在业务模块中直接使用__file__来初始化Exporter。以下是几种可行的实现方式:
方法1:封装工厂函数自动获取模块路径
通过inspect模块获取调用者的模块信息,自动提取模块文件路径,封装成工厂函数供业务模块调用:
# 在myproject.exporting中新增工厂函数 import inspect from .core import Exporter def get_exporter(): # 获取调用者的栈帧,进而拿到模块对象 caller_frame = inspect.currentframe().f_back caller_module = inspect.getmodule(caller_frame) # 利用模块的__spec__.origin获取文件路径(Python 3.4+支持) return Exporter(path=caller_module.__spec__.origin)
业务模块中使用时,直接调用工厂函数即可:
from myproject.exporting import get_exporter exporter = get_exporter() export = exporter.decorator() @export def public_function(...): """ 此函数已导出,为公共函数。 """ pass def private_function(...): """ 此函数为私有函数。 """ pass __all__, __dir__ = exporter.all_and_dir()
方法2:模块级装饰器彻底消除样板代码
封装一个模块装饰器,自动完成Exporter初始化、导出装饰器绑定、__all__和__dir__设置的全流程,业务模块只需标记需要导出的函数:
# 在myproject.exporting中实现模块装饰器 import inspect import atexit from .core import Exporter def exportable_module(): def decorator(module): # 初始化Exporter exporter = Exporter(path=module.__spec__.origin) export_decorator = exporter.decorator() # 将export装饰器绑定到模块全局 module.__dict__['export'] = export_decorator # 模块加载完成后自动设置__all__和__dir__ def _finalize(): module.__all__, module.__dir__ = exporter.all_and_dir() atexit.register(_finalize) return module return decorator
业务模块的使用方式极度简化:
from myproject.exporting import exportable_module # 标记当前模块为可导出模块 __name__ = exportable_module()(__name__) @export def public_function(...): """ 此函数已导出,为公共函数。 """ pass def private_function(...): """ 此函数为私有函数。 """ pass
方法3:利用模块级__getattr__延迟初始化
借助Python 3.7+支持的模块级__getattr__,在第一次访问export装饰器时才初始化Exporter,完全隐藏初始化逻辑:
from myproject.exporting import Exporter import inspect _exporter = None def __getattr__(name): global _exporter if name == 'export': if _exporter is None: # 获取当前模块对象并提取路径 current_module = inspect.getmodule(__getattr__) _exporter = Exporter(path=current_module.__spec__.origin) return _exporter.decorator() raise AttributeError(f"模块 {__name__} 不存在属性 {name}") # 模块退出时自动设置__all__和__dir__ import atexit def _finalize_export(): global _exporter if _exporter is not None: globals()['__all__'], globals()['__dir__'] = _exporter.all_and_dir() atexit.register(_finalize_export)
业务模块中只需直接使用@export装饰器,无需任何额外初始化代码:
from myproject.exporting import export @export def public_function(...): """ 此函数已导出,为公共函数。 """ pass def private_function(...): """ 此函数为私有函数。 """ pass
以上方案均通过inspect模块和模块的__spec__属性获取文件路径,避开了直接使用__file__的问题,同时符合现代Python的编码规范,不会触发代码检查工具的警告。
内容的提问来源于stack exchange,提问作者fish2000
相关产品推荐
相关产品推荐

