- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
rdkit.Chem.FragmentMatcher是 RDKit Python 层提供的一个轻量级 API,用于对分子做"片段命中 + 排除片段"的复合结构匹配:先用一个 SMARTS 图案作为命中条件(on-pattern),再叠加若干 SMARTS 排除图案(exclusion patterns),快速判断某分子是否含有目标片段且不含有被禁止的结构。读完本文,你可以掌握FragmentMatcher的全部方法语义、边界行为与底层调用链,并能将其用于官能团检测、数据集过滤等实际场景。
模块定位与文档组织方式
在 RDKit 的文档体系中,rdkit.Chem.FragmentMatcher的 API 参考页由 Sphinx 的automodule指令直接从模块源码生成。文档入口文件 Docs/Book/source/rdkit.Chem.FragmentMatcher.rst 的全部内容就是:
.. automodule:: rdkit.Chem.FragmentMatcher :members: :undoc-members: :show-inheritance:也就是说,渲染出的页面内容 = 模块 docstring + 全部成员签名。模块真正的"文档正文"是 rdkit/Chem/FragmentMatcher.py 开头那段包含 doctest 的 docstring(第 10~46 行),它同时也是模块的自动化测试。该页面在 rdkit.Chem 包目录 的 Submodules toctree 中注册(第 46 行)。
模块 docstring 中的完整用法(doctest 即文档)
模块 docstring 给出了FragmentMatcher的"最小使用契约",以下示例全部来自源码原样保留(注意 doctest 中Chem依赖模块级from rdkit import Chem导入):
1. 构造命中图案。如果你想匹配含C=O(羰基)的分子:
from rdkit.Chem import FragmentMatcher p = FragmentMatcher() p.Init('C=O')2. 判断分子是否命中:
from rdkit import Chem mol = Chem.MolFromSmiles('CC(=O)O') # 乙酸 p.HasMatch(mol) # 1(命中) p.HasMatch(Chem.MolFromSmiles('CC(C)C')) # 0(异丁烷,无羰基)3. 获取匹配的原子索引:
len(p.GetMatches(Chem.MolFromSmiles('CC=O'))) # 1(乙醛只有 1 个 C=O) len(p.GetMatches(Chem.MolFromSmiles('O=CC=O'))) # 2(乙二醛有两个 C=O)4. 添加排除片段(SMARTS)。加上苯环排除后,任何含苯环的分子一律判负:
p.AddExclusion('c1ccccc1') p.HasMatch(Chem.MolFromSmiles('CC=O')) # 1 p.HasMatch(Chem.MolFromSmiles('c1ccccc1CC=O')) # 0(苯乙醛被排除)方法逐一解析(结合源码实现)
FragmentMatcher类定义在 rdkit/Chem/FragmentMatcher.py,整个类只有约 40 行,内部状态只有两个字段:
self._onPatt:命中图案(Chem.MolFromSmarts解析后的 query mol),初始为None;self._offPatts:排除图案列表,初始为空列表。
| 方法 | 源码位置 | 行为 | 未调用Init时的返回值 |
|---|---|---|---|
Init(sma) | L59-L60 | 用 SMARTS 字符串设置/替换唯一的命中图案 | — |
AddExclusion(sma) | L56-L57 | 追加一个排除图案(可多次调用,累积生效) | — |
HasMatch(mol) | L68-L78 | 返回1或0(int,不是 bool) | 0 |
GetMatch(mol) | L80-L83 | 返回第一个匹配的原子索引元组 | None |
GetMatches(mol, uniquify=True) | L85-L88 | 返回全部匹配(元组列表),支持去重 | None |
GetBond(idx) | L90-L93 | 返回命中图案中指定索引的 bond,便于做"按图案内 atom map 提取原子"式的解析 | None |
GetSMARTS() | L62-L63 | 占位实现(函数体为pass,返回None) | None |
GetExclusionSMARTS() | L65-L66 | 占位实现(函数体为pass,返回None) | None |
需要特别说明的源码事实:
Init是替换语义:self._onPatt = Chem.MolFromSmarts(sma)直接赋值,重复调用Init会覆盖此前的命中图案;而AddExclusion是append语义,排除图案持续累积。GetSMARTS/GetExclusionSMARTS尚未实现:两者函数体均为pass。如果需要在序列化后恢复 SMARTS 原文,应自行保存构造时传入的字符串,而不能依赖这两个方法。GetMatches的uniquify参数原样透传给mol.GetSubstructMatches(query, uniquify=uniquify),默认True,即对同一组原子索引的不同排列只保留一个匹配;关闭去重后对对称图案会得到更多条目。
匹配判定逻辑与两个关键细节
判定顺序:先命中、后排除
HasMatch的完整判定逻辑(L68-L78):
def HasMatch(self, mol): if self._onPatt is None: return 0 t = mol.HasSubstructMatch(self._onPatt) if not t: return 0 else: for patt in self._offPatts: if mol.HasSubstructMatch(patt): return 0 return 1从源码结构看,匹配是"短路式"的:命中图案不匹配立即返回 0,不必检查排除图案;只有命中图案匹配成功后,才会逐个检查排除图案,任一排除图案命中即整体判 0。
细节一:排除图案作用于整个分子,而非匹配区域
值得注意的实现细节是:排除图案的mol.HasSubstructMatch(patt)是针对整张分子图执行的(第 75~77 行),而不是只检查命中片段内部。换句话说,AddExclusion的语义是"只要分子的任何位置出现该结构就整体否决",而不是"命中片段的邻域内不得出现该结构"。对苯环排除的例子(苯乙醛判 0)来说两者等价,但如果排除图案恰好就是命中图案的子结构、或者你期望做"片段局部排除",就要清楚FragmentMatcher不提供局部排除——这种更细粒度的需求需要用mol.GetSubstructMatches拿到匹配后再逐匹配手工判断。
细节二:未Init时的安全退化
四个查询方法(HasMatch/GetMatch/GetMatches/GetBond)开头都有if self._onPatt is None守卫,分别返回0或None。这意味着"空匹配器"永远不会抛异常,可以安全地用于"条件式启用"的过滤管道中——这一点从源码可以直接确认,无需额外防御。
底层调用链
FragmentMatcher本身不做图匹配,真正的子结构匹配委托给 C++ 核心ROMol的查询 API:
mol.HasSubstructMatch(query)/mol.GetSubstructMatch(query)/mol.GetSubstructMatches(query, uniquify=...)绑定自 C++ 的RWMol::HasSubstructMatch等成员;- Python 侧的包装实现可见 Code/GraphMol/Wrap/substructmethods.h,其中
GetSubstructMatches的uniquify参数(默认true)与 Python 层暴露的行为一致。
因此FragmentMatcher的开销模型是:Init时做一次 SMARTS 解析(MolFromSmarts会产生解析告警日志若字符串非法),每次HasMatch最多执行 1 + N 次子结构查询(N 为排除图案数),每次GetMatches执行 1 次子结构查询。
测试集成:docstring 如何成为回归测试
这个模块的 docstring 不只是文档,还是被纳入正式测试套件的 doctest:
- 模块内自测:文件末尾的 doctest boilerplate(L100-L109)允许直接
python rdkit/Chem/FragmentMatcher.py运行,_test()调用doctest.testmod(sys.modules["__main__"])并把失败数作为退出码。 - pytest 注册:rdkit/Chem/UnitTestDocTestsChem.py 在
load_tests中执行tests.addTests(doctest.DocTestSuite(FragmentMatcher, optionflags=doctest.ELLIPSIS))(第 15 行),使上述 docstring 示例随整个 Chem 测试套件一起回归。 - 运行配置:rdkit/pytest.ini 中
python_files = UnitTest*.py且addopts包含--doctest-modules,并配置了doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE;套件级入口脚本为 Scripts/run_python_tests.py。
这也解释了为什么 docstring 里的示例返回值写的是1/0而不是True/False——它们必须与HasMatch的实际 int 返回严格一致,否则 CI 中的 doctest 会失败。
实用建议与边界情况
- 返回类型差异:
HasMatch返回 int(0/1),可直接参与计数(如sum(matcher.HasMatch(m) for m in mols));GetMatch返回单个元组或None;GetMatches返回元组列表或None。注意GetMatches在未Init时返回None而非空列表,调用处应做判空。 - SMARTS 而非 SMILES:
Init与AddExclusion都用Chem.MolFromSmarts解析,所以可以写通配原子([#6]、[CX3])、环成员、方向性基团等 SMARTS 语法;写成普通 SMILES 时行为等价于将其解释为 SMARTS。 - 图案原子映射:拿到匹配后想按"图案中的第 i 个原子"提取分子中的对应原子,
GetBond(idx)提供了按图案索引取 bond 的能力,配合匹配元组中的图案侧索引即可定位;这是该 API 相比裸调HasSubstructMatch的少量但真实的便利性差异。 - 适用场景:该模块适合"有官能团且无某结构"这类二元条件过滤,例如"含有酮羰基(
C(=O))且不含芳香环"的候选集筛选。对于需要多图案同时命中、条件组合(AND/OR/NOT 嵌套)或基于匹配位置的后续处理,应直接使用rdkit.Chem的原生子结构查询接口;FragmentMatcher的价值在于把"on + 若干 off"这一常见模式封装成了状态化的对象。
参考文件索引
| 内容 | 路径 |
|---|---|
| 模块源码(类实现与 doctest) | rdkit/Chem/FragmentMatcher.py |
| Sphinx 文档页 | Docs/Book/source/rdkit.Chem.FragmentMatcher.rst |
| rdkit.Chem 包目录(页面注册处) | Docs/Book/source/rdkit.Chem.rst |
| doctest 回归注册 | rdkit/Chem/UnitTestDocTestsChem.py |
| pytest doctest 配置 | rdkit/pytest.ini |
| Python 测试套件入口 | Scripts/run_python_tests.py |
| C++ 子结构查询 Python 包装 | Code/GraphMol/Wrap/substructmethods.h |
- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
相关推荐
RDKit rdkit.Chem.Fraggle 包解析:基于片段化与 Tversky 相似度的分子检索算法
RDKit rdkit.Chem.Fraggle 包解析:基于片段化与 Tversky 相似度的分子检索算法 本文以 RDKit 官方文档树中的 rdkit.C
科学计算科研机器学习Wireit glob模式完全指南:文件匹配语法与排除规则详解
Wireit glob模式完全指南:文件匹配语法与排除规则详解 Wireit 是一款强大的 npm/pnpm/yarn 脚本升级工具,它通过智能的 glob 模
构建工具开发工具Kubernetes Python Client 详解:V1GroupSubject 模型与基于用户组的请求匹配
Kubernetes Python Client 详解:V1GroupSubject 模型与基于用户组的请求匹配 导读 本文基于官方 Python 客户端库(k
后端云原生容器编排
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考