You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python高效将SELFIES字符串分词为列表?

SELFIES字符串高效分词方法对比

我正在使用SELFIES(自引用嵌入字符串),它是分子的一种字符串表示形式,由方括号包裹的一系列token组成,比如丙烷可表示为 "[C][C][C]"。我的需求是将这类字符串高效转换为对应的token列表,示例如下:

selfies = "[C][C][C]"
tokens = some_function(selfies)
tokens  # 输出:["[C]","[C]","[C]"]

我测试了多种实现方法,以下是详细对比:


方法1:SELFIES原生分词函数

使用SELFIES库自带的分词函数,该函数会处理SELFIES中的分隔符.,但我的场景中不会出现该字符。

def split_selfies(selfies: str) -> Iterator[str]:
    """Tokenizes a SELFIES string into its individual symbols.
    :param selfies: a SELFIES string.
    :return: the symbols of the SELFIES string one-by-one with order preserved.
    :Example:
    >>> import selfies as sf
    >>> list(sf.split_selfies("[C][=C][F].[C]"))
    ['[C]', '[=C]', '[F]', '.', '[C]']
    """

    left_idx = selfies.find("[")

    while 0 <= left_idx < len(selfies):
        right_idx = selfies.find("]", left_idx + 1)
        if right_idx == -1:
            raise ValueError("malformed SELFIES string, hanging '[' bracket")

        next_symbol = selfies[left_idx: right_idx + 1]
        yield next_symbol

        left_idx = right_idx + 1
        if selfies[left_idx: left_idx + 1] == ".":
            yield "."
            left_idx += 1

性能测试:

%%timeit
tokens = list(sf.split_selfies(selfies))
# 3.41 µs ± 22.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

该方法速度较慢,推测是因为需要将生成器转换为列表。


方法2:库开发者实现的分词函数

这是SELFIES相关项目中开发者提供的实现,针对无.的场景优化。

def get_selfie_chars(selfies):
    '''Obtain a list of all selfie characters in string selfie
    
    Parameters: 
    selfie (string) : A selfie string - representing a molecule 
    
    Example: 
    >>> get_selfie_chars('[C][=C][C][=C][C][=C][Ring1][Branch1_1]')
    ['[C]', '[=C]', '[C]', '[=C]', '[C]', '[=C]', '[Ring1]', '[Branch1_1]']
    
    Returns:
    chars_selfie: list of selfie characters present in molecule selfie
    '''
    chars_selfie = []  # A list of all SELFIE sybols from string selfie
    while selfies != '':
        chars_selfie.append(selfies[selfies.find('['): selfies.find(']')+1])
        selfies = selfies[selfies.find(']')+1:]
    return chars_selfie

性能测试:

%%timeit
tokens = get_selfie_chars(selfies)
# 3.44 µs ± 43.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

耗时和原生函数基本持平。


方法3:自定义列表推导+split实现

我自己结合列表推导、切片和split()实现的方法,针对无.的场景优化。

def selfies_split(selfies):
    return [block+"]" for block in selfies.split("]")][:-1]

性能测试:

%%timeit
tokens = selfies_split(selfies)
# 1.05 µs ± 53.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

速度比前两种方法快约3倍。


补充方法:社区建议的三种实现

方法4:字符串替换+split

def stackoverflow_1_split(selfies):
    atoms = selfies[1:-1].replace('][', "$").split("$")
    return list(map('[{}]'.format, atoms))

性能测试:

%%timeit
tokens = stackoverflow_1_split(selfies)
# 1.75 µs ± 101 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

若不转换为列表,该方法速度更快,但列表是必需输出格式。

方法5:正则表达式匹配

import re
def stackoverflow_2_split(selfies):
    return re.findall(r".*?]", selfies)

性能测试:

%%timeit
tokens = stackoverflow_2_split(selfies)
# 1.81 µs ± 110 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

正则表达式方案并未优于其他方法,性能不如自定义的方法3。

方法6:字符串替换+空格分割(目前最快)

def stackoverflow_3_split(selfies):
    return selfies.replace(']', '] ').split()

性能测试:

%%timeit
tokens = stackoverflow_3_split(selfies)
# 485 ns ± 4.04 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

这是目前测试中最快的方案,速度约为方法3的2倍。


内容的提问来源于stack exchange,提问作者Etienne Reboul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:55:25