使用 vanilla Python 库进行字符到子串的翻译

Character-to-substring translation using vanilla Python libraries

问题

仅使用 vanilla Python 2.x(其中 x >= 7)内置的模块和函数,而无需滚动我自己的 Python 函数来执行此操作(见下文),如何将字符翻译成类似于 urllib.quote(), but limited to a subset of the characters that urllib.quote() 翻译的字符串?

在下面的示例中,它正在进行 url 编码,但我的问题恰好比 url 编码更基本和更笼统。在一般情况下,我想将任意指定的字符转换为任意指定的字符串,而不仅仅是那些符合 url 编码的 RFC 合规性。

例子

在下面的示例中,我只翻译平衡的括号字符(方括号、curly 括号和圆括号),而不是所有其他需要为 [=69= 引用的字符] 查询字符串。这根本与网络无关,而是针对 Python 脚本生成的输出的 paren navigation

我想我可以使用一些看起来既简洁又高效的东西,它将内置到 vanilla Python 2.x(vanilla 不需要安装额外的模块)使用类似于 string translate,但后者要求翻译 table 是从单个字符到单个字符的映射,而不是我想要的从单个字符到多个字符串的映射。

所以我在下面写了自己的:

def urlencode_parens(line):
    """url encode parens as would urllib.quote would do, but we only want it for parens"""
    trans_table = {
        '(': '%28',
        ')': '%29',
        '{': '%7B',
        '}': '%7D',
        '[': '%5B',
        ']': '%5D',
    }
    retval = []
    for char in line:
        if char in trans_table:
            char = trans_table[char]
        retval.append(char)
    return "".join(retval)

虽然基本的时序分析显示上面的速度非常快,但让我感到困扰的是我必须首先将其编码(因为现在我必须将其隐藏在我自己的个人模块集中并维护它).

我尝试过的东西

我调查了如何将 urllib.quote() 强制转换为 翻译上述字符,但它似乎在内部硬编码了这些字符的翻译而没有任何方法 extend/customize它。

我可以使用 re.sub() 来做到这一点,但我必须将它们链接起来,无论如何,考虑到 Python 中字符串的不变性。它导致代码看起来更像 Lisp 而不是 Python(不是说 Lisp 不好,只是 "When in Rome ... etc.")。鉴于正则表达式翻译可能涉及对正则表达式的重复重新编译,我放弃了它,因为我认为它的性能可能不如我上面编写的那样。

更新#1:maketrans 文档字符串具有误导性and/or不正确

看着 string.maketrans 我明白了:

这给出 no 指示 to 参数是可选的。它也没有说明如何使用 from 字段,正如 答案所指出的那样。

在 python 3 你可以这样做:

如果你只传递一个参数给str.maketrans(一个映射字典)你也可以有多个字符:

trans_dict = {
        '(': '%28',
        ')': '%29',
        '{': '%7B',
        '}': '%7D',
        '[': '%5B',
        ']': '%5D',
    }

trans_table = str.maketrans(trans_dict)

print('dict={} tuple=()'.translate(trans_table))
# dict=%7B%7D tuple=%28%29

在 python 2.7 你可以尝试使用 unicode.translate:

trans_dict = {
        '(': '%28',
        ')': '%29',
        '{': '%7B',
        '}': '%7D',
        '[': '%5B',
        ']': '%5D',
    }

trans_table = {ord(char): unicode(repl) for char, repl in trans_dict.items()}
print(u'dict={} tuple=()'.translate(trans_table))