如何构建Python脚本使配置可全局供所有函数调用?
嘿,这个优化点抓得很准——重复加载配置确实会浪费资源,尤其是当目录下文件数量多的时候。这里给你几个实用的方案,能轻松实现只加载一次CSV配置:
方案1:模块级全局加载
这是最简单的方式,直接在脚本的模块层面加载配置文件。脚本启动时会执行这部分代码一次,之后所有函数都可以直接共用这个配置变量。
示例代码:
import csv # 模块初始化时加载配置,整个脚本生命周期只执行一次 config = {} with open("config.csv", "r") as f: reader = csv.DictReader(f) # 假设CSV是key-value结构,根据你的实际格式调整 for row in reader: config[row["setting_key"]] = row["setting_value"] def test(dict_data): # 直接使用全局的config变量 print(f"用配置{config}测试数据:{dict_data}") def OpenFile(file_path): # 模拟读取文件并返回字典 file_content = {"file": file_path, "content": "sample data"} test(file_content) return file_content def GetFiles(target_dir): # 模拟获取目录下的文件列表 files = ["file1.txt", "file2.txt", "file3.txt"] for file in files: OpenFile(file) if __name__ == "__main__": GetFiles("./my_dir")
优点:零额外逻辑,简单直接;缺点:如果配置文件路径需要通过CLI参数动态指定(比如用户输入--config path/to/file.csv),这种方式就不太灵活,因为模块加载早于CLI参数解析。
方案2:初始化函数+全局变量
如果需要支持动态配置路径,可以写一个专门的加载函数,在脚本启动初期(比如解析完CLI参数后)调用一次,把配置存在全局变量里。还可以加个判断,确保即使不小心重复调用加载函数,也不会重复读取文件。
示例代码:
import csv # 初始化全局配置变量为None config = None def load_config(config_path): global config # 只有当config未加载时才执行读取操作 if config is None: with open(config_path, "r") as f: reader = csv.DictReader(f) config = {row["setting_key"]: row["setting_value"] for row in reader} return config def test(dict_data): if not config: raise ValueError("请先调用load_config()加载配置!") print(f"用配置{config}测试数据:{dict_data}") def OpenFile(file_path): file_content = {"file": file_path, "content": "sample data"} test(file_content) return file_content def GetFiles(target_dir): files = ["file1.txt", "file2.txt", "file3.txt"] for file in files: OpenFile(file) if __name__ == "__main__": # 模拟解析CLI参数得到配置路径 user_config_path = "./custom_config.csv" load_config(user_config_path) GetFiles("./my_dir")
优点:支持动态配置路径,且能防止重复加载;缺点:全局变量在复杂脚本里可能会带来维护问题,比如不小心被修改。
方案3:面向对象封装
把配置和相关函数封装到类里,配置在类实例化时加载一次,所有实例方法都可以共用这个配置。这种方式更清晰,也更容易扩展后续功能。
示例代码:
import csv class FileTestProcessor: def __init__(self, config_path): # 实例化时加载配置,每个实例只加载一次 self.config = {} with open(config_path, "r") as f: reader = csv.DictReader(f) self.config = {row["setting_key"]: row["setting_value"] for row in reader} def _test(self, dict_data): # 内部测试方法,使用实例的config属性 print(f"用配置{self.config}测试数据:{dict_data}") def open_file(self, file_path): file_content = {"file": file_path, "content": "sample data"} self._test(file_content) return file_content def get_files(self, target_dir): files = ["file1.txt", "file2.txt", "file3.txt"] for file in files: self.open_file(file) if __name__ == "__main__": processor = FileTestProcessor("./config.csv") processor.get_files("./my_dir")
优点:配置和逻辑绑定,代码结构清晰,避免全局变量污染;缺点:如果你的脚本很简单,可能会觉得有点“重”,但长期来看更利于维护。
方案4:用缓存装饰器缓存加载结果
如果你的配置加载函数需要处理不同的路径,且相同路径只需要加载一次,可以用functools.lru_cache装饰器来缓存加载结果。
示例代码:
import csv from functools import lru_cache # 用lru_cache缓存加载结果,相同config_path只会加载一次 @lru_cache(maxsize=None) def load_config(config_path): config = {} with open(config_path, "r") as f: reader = csv.DictReader(f) config = {row["setting_key"]: row["setting_value"] for row in reader} return config def test(dict_data, config_path): config = load_config(config_path) print(f"用配置{config}测试数据:{dict_data}") def OpenFile(file_path, config_path): file_content = {"file": file_path, "content": "sample data"} test(file_content, config_path) return file_content def GetFiles(target_dir, config_path): files = ["file1.txt", "file2.txt", "file3.txt"] for file in files: OpenFile(file, config_path) if __name__ == "__main__": GetFiles("./my_dir", "./config.csv")
优点:自动缓存相同路径的配置,无需手动判断;缺点:配置文件如果在运行时被修改,缓存不会自动更新(如果需要支持热更新,得额外处理缓存失效)。
选择建议
- 如果配置路径固定,方案1最省事;
- 如果需要动态指定配置路径,方案2或方案3更合适;
- 如果要处理多个配置路径且需要缓存,方案4是不错的选择。
内容的提问来源于stack exchange,提问作者xtian

