ElementTree 修改属性应直接操作 attrib 字典,避免用 set() 清空其他属性;带命名空间需传入 ns 字典;write() 必须指定 encoding='utf-8' 和 xml_declaration=True;修改节点前确认是否为原树引用。

直接改 attrib 字典,别用 set() 覆盖整个属性集
ElementTree 修改属性最常见错误是以为必须调用 set() 才能改值,结果把其他已有属性全清空了。其实每个元素的 attrib 就是个普通字典,直接赋值更安全、更直观。
- 想改单个属性:
elem.attrib['id'] = 'new-123'—— 不影响class、data-*等其他属性 - 误用
set():elem.set('id', 'new-123')看似没问题,但如果你后续又elem.set('class', 'active'),中间没有保存或重新读取,容易漏掉别的属性逻辑 - 批量更新多个属性时,直接操作
attrib更可控:elem.attrib.update({'id': 'x', 'data-status': 'done'})
查找目标节点前先确认命名空间是否干扰 find() 和 findall()
XML 带命名空间(比如 xmlns="http://example.com/ns")时,find('item') 会返回 None,不是语法错,是匹配失败。
- 不带命名空间的 XML:直接用
root.find('user')没问题 - 带默认命名空间的 XML:必须传入命名空间字典,例如
ns = {'ns': 'http://example.com/ns'},再写root.find('ns:user', ns) - 用
root.iter()可绕过命名空间限制,但效率略低;适合不确定结构或需深度遍历的场景 - 检查是否有命名空间:打印
root.tag,如果看到类似{http://example.com/ns}root的格式,说明已被解析进 tag 名里,此时要用带命名空间的查找方式
write() 输出中文乱码?记得指定 encoding='utf-8' 和 xml_declaration=True
用 tree.write('out.xml') 直接保存,中文常变成字符实体或乱码,根本原因是默认编码不是 UTF-8,且没写 XML 声明头。
- 正确写法:
tree.write('out.xml', encoding='utf-8', xml_declaration=True) - 缺
xml_declaration=True:生成文件开头没<?xml version='1.0' encoding='utf-8'?>,某些解析器会按系统默认编码(如 cp1252)读,中文就崩 - 只写
encoding='utf-8'不加声明:文件内容是 UTF-8,但没声明,解析行为不可控 - 如果输出要被浏览器或旧系统读,避免用
short_empty_elements=False(默认为True),否则<tag/>可能变<tag></tag>,虽合法但可能触发下游兼容问题
修改后没生效?检查是否在操作副本而非原树节点
常见于用 findall() 或 iter() 获取节点列表后,误以为修改列表里元素就能自动同步到原树——其实可以,但前提是没做深拷贝或重建。
立即学习“Python免费学习笔记(深入)”;
- 这样是 OK 的:
for elem in root.findall('.//price'): elem.text = '99.9'——elem是原树引用 - 这样会失效:
nodes = [e for e in root.iter('price')]; for n in nodes: n.text = '99.9'看似一样,但如果前面某步用了copy.deepcopy()或ET.fromstring(ET.tostring(...)),那nodes就是新树里的节点,改了也不影响原始root - 调试技巧:改完后打印
ET.tostring(root, encoding='unicode')看实际输出,比只看变量名更可靠 - 如果涉及多次解析/序列化循环,注意
ET.fromstring()每次都新建树,不要混用不同树的节点
改属性本身很简单,难的是搞清你操作的到底是不是那个“真节点”,以及它所处的上下文有没有命名空间、编码、引用关系这些隐形约束。








