Python如何导入可触发函数执行、无需用户实例化类的变量
实现方案
方案1:Python 3.7+ 模块__getattr__实现(最简方案)
Python 3.7及以上版本支持模块级别的__getattr__方法,当访问模块中不存在的属性时会自动触发该方法,我们可以用这个特性实现变量访问时的自动逻辑注入,全程对调用方透明。
改写后的mnist.py代码如下:
import os # 按需引入你的下载逻辑依赖,比如requests # 私有变量缓存已加载的内容,避免重复执行逻辑 _images = None _labels = None def _check_and_download(file_path): """自定义文件校验、下载逻辑,可根据实际需求修改""" if not os.path.exists(file_path): print(f"文件{file_path}不存在,正在下载...") # 此处补充你的下载、存储逻辑 # 下载完成后读取文件内容返回,下方为示例返回值 return f"{file_path}的实际内容" def __getattr__(name): global _images, _labels if name == "images": if _images is None: _images = _check_and_download("./mnist_images.npy") return _images elif name == "labels": if _labels is None: _labels = _check_and_download("./mnist_labels.npy") return _labels elif name == "show": def show(): print("Im showing your image") return show raise AttributeError(f"module {__name__} has no attribute {name}")
调用方run.py代码完全不需要修改,和原有写法完全一致:
from examples.mnist import images, labels, show print(images) print(labels) show()
第一次访问images/labels时会自动执行检查、下载逻辑,后续访问会直接返回已经加载好的缓存值,不会重复执行逻辑。
方案2:兼容全Python版本的模块实例替换方案
如果需要兼容Python 3.7以下版本,可以将当前模块对象替换为一个带属性getter的类实例,实现同样的效果:
改写后的mnist.py代码如下:
import os import sys class _MNISTModule: def __init__(self): self._images = None self._labels = None def _check_and_download(self, file_path): """和方案1的校验、下载逻辑完全一致""" if not os.path.exists(file_path): print(f"文件{file_path}不存在,正在下载...") # 下载逻辑略 return f"{file_path}的实际内容" @property def images(self): if self._images is None: self._images = self._check_and_download("./mnist_images.npy") return self._images @property def labels(self): if self._labels is None: self._labels = self._check_and_download("./mnist_labels.npy") return self._labels def show(self): print("Im showing your image") # 把当前模块替换为类实例,用户导入时拿到的就是该实例 sys.modules[__name__] = _MNISTModule()
调用方代码同样不需要做任何修改,使用体验和普通模块变量完全一致。
内容的提问来源于stack exchange,提问作者Daniel Wilczak
相关产品推荐
相关产品推荐

