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

Python使用yield递归解析XML时为何会跳过递归逻辑?

问题描述

给定如下XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:okp="okapi-framework:xliff-extensions" xmlns:its="http://www.w3.org/2005/11/its" xmlns:itsxlf="http://www.w3.org/ns/its-xliff/" its:version="2.0">
<file original="temp/file_conversion/tmp4a9kn6bn/69502fea-751c-4c3c-a38a-4fce9e13ebde.txt" source-language="en" target-language="ar" datatype="x-text/plain" okp:inputEncoding="UTF-8">
<body>
<trans-unit id="1idhasofh" xml:space="preserve">
<source xml:lang="en">foo<bpt id="0">&lt;bar&gt;</bpt>&lt;Instruction&gt;<ept id="0">&lt;crow&gt;</ept>&lt;grande&gt;</source>
<target xml:lang="ar">foo<bpt id="0">&lt;bar&gt;</bpt>&lt;Instruction&gt;<ept id="0">&lt;crow&gt;</ept>&lt;grande&gt;</target>
</trans-unit>
</body>
</file>
</xliff>

编写了如下基于yield的XML解析函数:

from xml.etree import ElementTree as ET


def parse_xml(ele: ET.Element):
    tag = ele.tag
    if not isinstance(tag, str) and tag is not None:
        return
    t = ele.text
    if t:
        yield t
    for e in ele:
        parse_xml(e)
        t = e.tail
        if t:
            yield t

def main():
    fp = "path/to/xml"
    tree = ET.parse(fp)
    root = tree.getroot()

    t_units = root.findall(".//{*}trans-unit")
    for source, target in t_units:
        for ele in parse_xml(source):
            print(ele)

运行后输出缺失了部分内容:

foo
&lt;Instruction&gt;
&lt;grande&gt;

但将函数中的yield替换为print后:

def parse_xml(ele: ET.Element):
    tag = ele.tag
    if not isinstance(tag, str) and tag is not None:
        return
    t = ele.text
    if t:
        print(t)
    for e in ele:
        parse_xml(e)
        t = e.tail
        if t:
            print(t)

能得到预期的完整输出:

foo
&lt;bar&gt;
&lt;Instruction&gt;
&lt;crow&gt;
&lt;grande&gt;

疑问:为什么使用yield时递归逻辑会被跳过?

原因分析与修复

问题核心在于生成器函数的特性:调用生成器函数只会返回一个生成器对象,不会自动执行函数内的逻辑,必须通过迭代(比如for循环、yield from)才能触发它产出值。

在带yield的初始版本中,递归调用parse_xml(e)时,只是创建了一个生成器对象但没有处理它,导致递归函数里的yield产出的<bar>、<crow>这些值完全没有被传递到上层迭代器,自然不会出现在输出里。而print版本中,函数调用会直接执行内部的打印逻辑,所以所有内容都能正常输出。

修复方案

只需修改递归调用的部分,让递归生成器的产出值被传递到上层:

方案1:使用yield from(Python 3.3及以上)

yield from可以直接迭代生成器并产出所有值,是处理递归生成器的简洁方式:

def parse_xml(ele: ET.Element):
    tag = ele.tag
    if not isinstance(tag, str) and tag is not None:
        return
    t = ele.text
    if t:
        yield t
    for e in ele:
        yield from parse_xml(e)  # 传递递归生成器的所有产出值
        t = e.tail
        if t:
            yield t

方案2:手动迭代递归生成器

如果使用Python 3.2及以下版本,可以手动循环迭代递归生成器的每个值并yield:

def parse_xml(ele: ET.Element):
    tag = ele.tag
    if not isinstance(tag, str) and tag is not None:
        return
    t = ele.text
    if t:
        yield t
    for e in ele:
        for item in parse_xml(e):  # 手动迭代并产出递归生成器的每个值
            yield item
        t = e.tail
        if t:
            yield t

修改后运行代码,就能得到和print版本一致的完整输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:37:06