You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不依赖__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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:31:15