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

递归查找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"))

关键修改说明

  1. 遍历逻辑调整:不再直接返回第一个递归调用的结果,而是先存储结果,仅当结果非空时才返回,否则继续循环处理下一个_include文件。
  2. 路径拼接优化:使用os.path.join处理相对路径,避免手动拼接字符串导致的跨平台路径错误。
  3. 明确递归基线:当以下任一情况发生时,递归终止并返回None:
    • 当前YAML文件不存在或解析失败
    • 当前文件不含目标Key,且所有_include文件都遍历完毕仍未找到Key

测试结果

修复后运行代码,会依次打开:
./test.yaml → test/test.yaml → test/deeper/notexist.yaml → test/deeper/test.yaml,最终找到foo并返回该文件的绝对路径。


内容的提问来源于stack exchange,提问作者Harry Whitnear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 08:34:59