递归查找YAML指定key的函数异常:前置无目标文件时返回None
递归查找YAML文件指定Key:解决_include数组遍历中断问题
问题描述
我编写了一个递归函数,用于读取YAML文件并查找指定Key。若当前文件不存在目标Key,则检查_include字段,递归查找引用的YAML文件,直至找到Key或穷尽所有选项。但当_include数组中先列出不含目标Key的文件(如notexist.yaml)时,函数返回None,而实际目标Key存在于后续引用的文件中。我缺乏递归编程经验,不清楚该如何设置此场景下的递归基线。
原始代码
import yaml import os.path def load_yaml_data(yaml_path): """Read a YAML file and return the data""" try: with open(yaml_path, 'r', encoding="utf-8") as yaml_file: yaml_data = yaml.safe_load(yaml_file) return yaml_data except FileNotFoundError: return None except yaml.YAMLError as exc: return None def find_key_in_yaml_recursive(yaml_path, key): print(f"opening {yaml_path}") yaml_data = load_yaml_data(yaml_path) # print(f"Yaml data : {yaml_data}") if yaml_data: if key in yaml_data: print(f"{key} found in {yaml_path}") return yaml_path elif '_include' in yaml_data: for include in yaml_data['_include']: include_path = os.path.expandvars(include) if not os.path.isabs(include_path): include_path = os.path.abspath('/'.join([os.path.dirname(yaml_path), include_path])) return find_key_in_yaml_recursive(include_path, key) if __name__ == "__main__": print(find_key_in_yaml_recursive("./test.yaml", "foo"))
目录结构
. | test.yaml └─── test │ │ test.yaml │ └─── deeper │ │ test.yaml │ │ notexist.yaml
YAML文件内容
./test.yaml
--- _include: - "test/test.yaml" not_foo: - "some data"
./test/test.yaml
--- _include: - ./deeper/notexist.yaml - ./deeper/test.yaml foop: - "somedata"
./test/deeper/notexist.yaml
--- nothinguseful: - data
./test/deeper/test.yaml
--- foo: - some data somethingelse: - data
目标Keyfoo存在于./test/deeper/test.yaml中,但函数返回None。
问题根源
你的代码在遍历_include数组时,第一个文件递归查找后直接return结果。如果第一个文件没找到Key,函数就直接返回None,完全不会继续检查数组里的后续文件。比如在./test/test.yaml的_include中,先查notexist.yaml,这个文件没有foo,递归返回None,函数就直接把None返回了,根本没去查后面的deeper/test.yaml。
修复方案
修改递归函数的遍历逻辑:遍历_include数组时,只有当递归找到Key(返回非None值)时才立即返回;如果当前文件没找到,继续遍历下一个文件,直到所有文件都遍历完再返回None。
修复后的代码
import yaml import os.path def load_yaml_data(yaml_path): """Read a YAML file and return the data""" try: with open(yaml_path, 'r', encoding="utf-8") as yaml_file: yaml_data = yaml.safe_load(yaml_file) return yaml_data except FileNotFoundError: return None except yaml.YAMLError as exc: return None def find_key_in_yaml_recursive(yaml_path, key): print(f"opening {yaml_path}") yaml_data = load_yaml_data(yaml_path) if yaml_data: if key in yaml_data: print(f"{key} found in {yaml_path}") return yaml_path elif '_include' in yaml_data: for include in yaml_data['_include']: include_path = os.path.expandvars(include) if not os.path.isabs(include_path): # 用os.path.join替代手动拼接,避免路径分隔符问题 include_path = os.path.abspath(os.path.join(os.path.dirname(yaml_path), include_path)) result = find_key_in_yaml_recursive(include_path, key) # 找到结果才返回,否则继续遍历下一个文件 if result is not None: return result # 所有情况都未找到,返回None(递归基线) return None if __name__ == "__main__": print(find_key_in_yaml_recursive("./test.yaml", "foo"))
关键修改说明
- 遍历逻辑调整:不再直接返回第一个递归调用的结果,而是先存储结果,仅当结果非空时才返回,否则继续循环处理下一个
_include文件。 - 路径拼接优化:使用
os.path.join处理相对路径,避免手动拼接字符串导致的跨平台路径错误。 - 明确递归基线:当以下任一情况发生时,递归终止并返回None:
- 当前YAML文件不存在或解析失败
- 当前文件不含目标Key,且所有
_include文件都遍历完毕仍未找到Key
测试结果
修复后运行代码,会依次打开:./test.yaml → test/test.yaml → test/deeper/notexist.yaml → test/deeper/test.yaml,最终找到foo并返回该文件的绝对路径。
内容的提问来源于stack exchange,提问作者Harry Whitnear
相关产品推荐
相关产品推荐

