__getattr__ 可以实现包的懒加载,它的核心思想是:当用户在模块中访问一个尚未定义的属性时,Python 会调用 __getattr__(name) 方法。我们可以在这里拦截请求,动态导入对应的模块。
以我的 vvdutils 库为例,这个库中包含了 MongoGridFSConnection 模块,用户可以调用 from vvdutils import MongoGridFSConnection 引用该功能,如果用 __getattr__ 的视角来看这个过程是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| # ./__init__.py """ vvdutils 主模块。 数据库连接类已启用懒加载,仅在首次访问时导入。 """# 1. 定义公开的接口,这有助于工具进行代码分析、自动补全和生成文档。 # 这里列出所有你希望用户从包顶级直接访问的类名。 __all__ = [ 'MongoGridFSConnection', # 对应 mongofs.connect.MongoGridFSConnection 'MysqlConnection', # 对应 mysql.connect.MysqlConnection # 未来可以轻松地在这里添加新的类名,例如: # 'RedisConnection', # 'PostgresConnection', ] # 2. 核心:定义懒加载映射表。 # 格式:'对外暴露的属性名': ('模块路径', '实际类名') # 模块路径相对于当前包(vvdutils),使用点号分隔。 _LAZY_IMPORTS = { 'MongoGridFSConnection': ('mongofs.connect', 'MongoGridFSConnection'), 'MysqlConnection': ('mysql.connect', 'MysqlConnection'), # 未来添加新模块时,只需在此增加一行映射即可。 # 'RedisConnection': ('redis.connect', 'RedisConnection'), } # 3. 实现 __getattr__ 函数 def __getattr__(name: str): """ 当试图访问本模块中不存在的属性时被调用。 用于实现类(尤其是重量级数据库连接类)的懒加载。 """ if name in _LAZY_IMPORTS: # 根据映射表获取模块路径和类名 module_path, class_name = _LAZY_IMPORTS[name] # 动态导入模块。__import__ 返回顶级模块,getattr 逐级深入获取子模块。 # level=0 表示绝对导入。 module = __import__(module_path, fromlist=[class_name], level=0) # 从模块中获取我们需要的具体类 klass = getattr(module, class_name) # 【关键】将获取到的类缓存到本模块的全局变量中。 # 这样后续再次访问该属性时,Python会直接找到它,不会再触发 __getattr__。 globals()[name] = klass return klass # 如果请求的属性名不在我们的懒加载映射表中,则抛出标准的 AttributeError。 raise AttributeError(f"module '{__name__}' has no attribute '{name}'") # 4. 实现 __dir__ 函数 def __dir__(): """ 返回模块中可用的属性名列表。 这对于 IDE 的自动补全和交互式环境(如 IPython)的 tab 补全至关重要。 """ # 合并:内置属性、已加载的属性、以及我们声明支持懒加载但尚未加载的属性。 attrs = set(globals().keys()) # 当前已存在于全局作用域中的名称 attrs.update(_LAZY_IMPORTS.keys()) # 加上所有我们声明支持懒加载的名称 attrs.update(['__all__', '__doc__', '__getattr__', '__dir__']) # 确保特殊方法也在列表中 return sorted(attrs) # 【可选但推荐】初始化提示 # 可以在这里导入一些轻量级的工具模块,它们不影响启动速度。 # 例如,如果你的 `utils` 模块很轻量,可以正常导入: # from . import utils # 注意:我们在这里并没有导入 mongofs 或 mysql,它们将在被访问时按需加载。
|