etree.subelement 的 attrib 参数必须是内置 dict 类型,不可用 ordereddict 等映射类型;空字符串属性值不会被自动省略,需手动过滤。

etree.SubElement 的 attrib 参数必须是普通字典,不能是 OrderedDict 或其他映射类型
Python 3.7+ 虽然 dict 保持插入顺序,但 lxml 内部对 attrib 做了类型检查——只接受内置 dict。传入 collections.OrderedDict、defaultdict 甚至子类化的 dict,都会静默失败或触发意外行为(比如属性丢失、顺序错乱、甚至 segfault)。
实操建议:
- 显式用
dict()包裹:比如etree.SubElement(parent, "tag", attrib=dict(od)) - 避免直接继承
dict并重写__init__后传给attrib—— lxml 不认“像 dict 的东西”,只认type(attrib) is dict - 如果依赖顺序(如生成需严格按 schema 排列的 XML),别靠
OrderedDict,老实用普通dict(3.7+ 安全)并按需排序键名再构造
设置空字符串属性值时,lxml 不会自动省略该属性
XML 规范允许空值属性(如 <input disabled>),而 lxml 默认忠实输出。如果你期望“值为空就彻底不写这个属性”,得自己过滤。
常见错误现象:传入 {"disabled": ""},结果 XML 里真出现了 disabled="",但下游系统认为这是启用状态(比如某些浏览器解析逻辑)。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 预处理
attrib字典:用{k: v for k, v in attrs.items() if v not in ("", None)} - 不要依赖
None自动过滤——lxml把None当作字符串"None"渲染,不是删掉属性 - 若需兼容旧版 Python(sorted(attrs.items()) 构造列表,再转
dict
中文、特殊字符作为属性值时,无需手动编码,lxml 自动处理
很多人看到中文就下意识 urllib.parse.quote 或 .encode("utf-8"),反而导致双重编码(比如变成 %E4%BD%A0%E5%A5%BD 显示在 XML 里)。
lxml 内部使用 libxml2,原生支持 UTF-8,只要你的 Python 字符串是 str(不是 bytes),直接塞进去就对了。
实操建议:
- 确认变量类型是
str:用isinstance(val, str)检查,别传bytes - 如果从文件/网络读取的是
bytes,先用.decode("utf-8")(或对应编码)转成str,再进attrib - XML 声明里的 encoding(如
<?xml version="1.0" encoding="UTF-8"?>)由etree.tostring(..., encoding="utf-8")控制,和attrib值无关
用 **kwargs 传属性时,键名不能含冒号(:),否则报 ValueError: Invalid tag name
XML 命名空间前缀(如 xsi:type)不能直接当 keyword argument 键名,因为 : 在 Python 标识符中非法。但你又确实需要设置带命名空间的属性。
实操建议:
- 改用完整字典传参:
etree.SubElement(parent, "tag", attrib={"xsi:type": "xs:string"}) - 别试图用
**{"xsi:type": ...}—— 语法错误,Python 解析不过 - 如果批量生成带命名空间的属性,先构建字典再解包,别在调用点硬写
**
setattr 操作底层对象),libxml2 也要求命名空间已声明,否则序列化时可能静默丢弃该属性。










