如何修复Python中RecursionError:递归深度超出限制的问题?
我正在编写一个Python脚本,功能为读取XML文件、将部分变量替换为占位符、使用Google Translate API翻译文本、恢复变量后写入另一个XML文件。但运行脚本时出现如下错误:
Traceback (most recent call last):
File "C:\Users\lazar\AppData\Local\Programs\Python\Python311\Lib\xml\etree\ElementTree.py", line 757, in _get_writer
write = file_or_filename.write
^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'write'During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "\translate.py", line 48, in _serialize_xml
return ET._serialize_xml(write, elem, qnames, namespaces, short_empty_elements=short_empty_elements)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "\translate.py", line 48, in _serialize_xml
return ET._serialize_xml(write, elem, qnames, namespaces, short_empty_elements=short_empty_elements)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "./translate.py", line 48, in _serialize_xml
return ET._serialize_xml(write, elem, qnames, namespaces, short_empty_elements=short_empty_elements)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Previous line repeated 997 more times]
RecursionError: maximum recursion depth exceeded
我的代码如下:
import xml.etree.ElementTree as ET import re from google.cloud import translate_v2 as translate from google.oauth2 import service_account credentials_dict = { #keys goes here } credentials = service_account.Credentials.from_service_account_info(credentials_dict) client = translate.Client(credentials=credentials) tree = ET.parse("input.xml") root = tree.getroot() def replace_variables(text): variables = re.findall(r"\{.*?\}", text) for i, variable in enumerate(variables): text = text.replace(variable, "~" * (i + 1)) return text, variables def restore_variables(text, variables): placeholders = re.findall(r"~+", text) for i, placeholder in enumerate(placeholders): if len(variables) > i: text = text.replace(placeholder, variables[i]) return text def CDATA(text=None): element = ET.Element('![CDATA[') element.text = text return element def _serialize_xml(write, elem, qnames, namespaces, *, short_empty_elements=True): if elem.tag == '![CDATA[': write("\n<%s%s]]>\n" % (elem.tag, elem.text)) return return ET._serialize_xml(write, elem, qnames, namespaces, short_empty_elements=short_empty_elements) ET._serialize_xml = _serialize_xml new_elements = [] for i, element in enumerate(root.iter()): if element.text is not None: text = element.text.strip() modified_text, variables = replace_variables(text) translation = client.translate(modified_text, target_language="es") translated_text = translation["translatedText"] restored_text = restore_variables(translated_text, variables) print(text + " -> " + restored_text) cdata_element = CDATA(restored_text) new_element = element.makeelement(element.tag, element.attrib) new_element.append(cdata_element) new_elements.append(new_element) for i, new_element in enumerate(new_elements): if i < len(root): root[i] = new_element else: root.append(new_element) tree.write("output.xml", encoding="utf-8", xml_declaration=True)
错误原因分析
- 无限递归:重写
ET._serialize_xml时,直接调用了已经被替换为自定义函数的ET._serialize_xml,导致每次调用都会进入自身循环,触发递归深度超限。 - 元素替换逻辑错误:
root.iter()遍历所有XML元素(包括所有后代),但后续仅替换root的直接子元素,导致结构混乱、重复添加元素。 - CDATA序列化不规范:自定义的CDATA元素标签不符合ElementTree的规则,写入文件时触发
AttributeError。
修复后代码
import xml.etree.ElementTree as ET import re from google.cloud import translate_v2 as translate from google.oauth2 import service_account credentials_dict = { # 填入你的密钥信息 } credentials = service_account.Credentials.from_service_account_info(credentials_dict) client = translate.Client(credentials=credentials) # 保存原始序列化函数,避免递归调用 original_serialize_xml = ET._serialize_xml def replace_variables(text): variables = re.findall(r"\{.*?\}", text) # 使用语义化占位符,避免翻译时被误处理 for i, variable in enumerate(variables): text = text.replace(variable, f"__TRANSLATE_VAR_{i}__") return text, variables def restore_variables(text, variables): # 精准替换对应占位符 for i, variable in enumerate(variables): text = text.replace(f"__TRANSLATE_VAR_{i}__", variable) return text def CDATA(text=None): # 用注释元素模拟CDATA,配合序列化函数转换为标准格式 return ET.Comment(f"CDATA_MARKER:{text}") def _serialize_xml(write, elem, qnames, namespaces, *, short_empty_elements=True): # 处理自定义CDATA注释 if isinstance(elem, ET.Comment) and elem.text.startswith("CDATA_MARKER:"): cdata_content = elem.text.split("CDATA_MARKER:", 1)[1] write(f"\n<![CDATA[{cdata_content}]]>\n") return # 调用原始函数处理其他元素 original_serialize_xml(write, elem, qnames, namespaces, short_empty_elements=short_empty_elements) # 替换全局序列化函数 ET._serialize_xml = _serialize_xml tree = ET.parse("input.xml") root = tree.getroot() # 直接遍历修改原元素,避免结构混乱 for element in root.iter(): if element.text and element.text.strip(): raw_text = element.text.strip() modified_text, variables = replace_variables(raw_text) # 调用翻译API translation_result = client.translate(modified_text, target_language="es") translated_text = translation_result["translatedText"] # 恢复变量 final_text = restore_variables(translated_text, variables) print(f"{raw_text} -> {final_text}") # 清空原元素内容,添加CDATA element.clear() element.append(CDATA(final_text)) # 写入输出文件 tree.write("output.xml", encoding="utf-8", xml_declaration=True)
关键修复点
- 解决递归问题:提前保存原始序列化函数,自定义函数中调用原始函数而非自身,彻底终止无限递归。
- 优化占位符:使用
__TRANSLATE_VAR_0__这类语义化占位符,避免翻译时被误译或因占位符长度变化导致匹配失败。 - 简化元素修改:直接遍历并修改原XML元素,无需重新构建元素再替换,避免结构混乱。
- 规范CDATA处理:用注释元素模拟CDATA,通过序列化函数转换为标准CDATA格式,解决序列化时的属性错误。
额外优化建议
- 批量翻译:收集所有待翻译文本批量调用API,减少请求次数,提升效率。
- 异常处理:添加try-except块捕获API调用异常(网络错误、配额不足等),避免脚本崩溃。
- 正则增强:若变量格式复杂,调整正则表达式确保精准匹配(如处理嵌套变量、特殊字符)。
内容的提问来源于stack exchange,提问作者Lázaro

