如何用PEP 634结构模式匹配lxml HtmlElement指定属性
问题原因
当你尝试用case HtmlElement(tag='a', attrib={'href': href})进行匹配时,Python 3.11.4对C扩展实现的_Attrib类(lxml的属性容器)存在处理差异:
- 类模式的关键字参数中,嵌套的字典字面量
{'href': href}本应作为映射模式检查attrib的键值,但由于_Attrib是C扩展实现的Mapping子类,Python错误地将其转为相等比较(检查_Attrib实例是否等于普通字典{'href': href}),而_Attrib与普通字典的__eq__逻辑不同,导致匹配失败,无法捕获href。 - 直接用
match element.attrib: case Mapping({'href': href}):能成功,是因为Mapping()模式明确强制Python按映射类型的规则处理,绕过了相等比较的逻辑。
解决方案
方案1:显式用Mapping()包裹映射模式(推荐)
在类模式的属性中,用Mapping()明确指定按映射类型匹配,强制Python应用映射模式规则:
from collections.abc import Mapping from lxml.html import HtmlElement # 示例匹配代码 match target_element: case HtmlElement(tag='a', attrib=Mapping({'href': href})): print(href) # 正确提取href值
方案2:使用位置模式(若HtmlElement支持)
如果HtmlElement类定义了__match_args__ = ('tag', 'attrib'),可以用位置模式结合Mapping():
match target_element: case HtmlElement('a', Mapping({'href': href})): print(href)
方案3:猴子补丁_Attrib类(不推荐,仅作备选)
通过给_Attrib添加__match__方法,让它正确处理映射模式:
from collections.abc import Mapping from lxml.etree import _Attrib def _attrib_match(self, pattern): if isinstance(pattern, dict): bindings = {} for key, val_pattern in pattern.items(): if key not in self: return None # 处理值模式的匹配与变量绑定 match self[key]: case val_pattern as val: # 捕获变量(以下划线开头的模式变量) if hasattr(val_pattern, '__name__') and val_pattern.__name__.startswith('_'): bindings[val_pattern.__name__] = val case _: return None return bindings return None _Attrib.__match__ = _attrib_match # 之后可直接用原始语法匹配 match target_element: case HtmlElement(tag='a', attrib={'href': href}): print(href)
注意:猴子补丁可能影响lxml的其他行为,仅在必要时使用。
内容的提问来源于stack exchange,提问作者qyryq
相关产品推荐
相关产品推荐

