Python食谱-1.16.替换字符串中的变量

原文作者:Scott David Daniels
中文编译:Tony (digitalsatori)

问题

如何才能用简单的方法将字符串中的一些特殊标记的子串替换为一个字典中定义的字符串。

解决方法

以下的解决方法对Python2.3以上的版本有效:

def expand(format, d, marker='"', safe=False):
    if safe:
        def lookup(w): return d.get(w, w.join(marker*2))
    else:
        def lookup(w): return d[w]
    parts = format.split(marker)
    parts[1::2] = map(lookup, parts[1::2])
    return ''.join(parts)
if _ _name_ _ == '_ _main_ _':
    print expand('just "a" test', {'a': 'one'})
# emits: just one test

当参数 safe 为默认的 False时,每个标记的子串必须要在字典 d 中找到,否则 expand 函数会中止执行并返回一个 KeyError 的异常。当参数 safe 被传递了值 True 时,没有在字典中找到对应项的标记子串会保持原样。

讨论

expand 函数内部有些值得探讨的地方。它定义了两个内部的嵌套函数,函数名都为 lookup ,调用哪个嵌套函数取决于是否要求“safe“。Safe表示当标记子串没有在字典中找到时,不会抛出 KeyError异常。当不要求'safe'时(默认情况), lookup 直接对字典 d 索引查询,如果没有在字典中找到子串就会抛出异常。当 lookup 要求‘safe’时,它使用字典 d 的 get 方法, 并且提供一个默认值,这个默认值由子串与其两边的标记符(marker)构成。这样的话,当expand函数的safe参数被传递了 True 值后,未知标记子串不会引起异常,而会保持原样。 w.join(marker*2) 的一个显然的替代表示方法是: marker + w + marker

不管使用哪个版本的嵌套函数 lookup``, ``expand 函数都遵循Python字符串处理的重要方法:'分割-修改-连接'。对于 expand 函数来说,修改处理中使用了字符串切片(slice)的步进(step)。实际上 expand 函数访问处理了 parts 所有的奇数项,然后再将它们连接起来。因为这些奇数项的内容正好是原格式化字串的标记符对中的子串内容。它们也正是我们需要在字典中查询的对象。

本节中的 expand 所接受的格式化字串的语法要比 string.Template 函数所接受的以$为标记的格式化字串语法灵活。如果希望在格式化字串中包含双引号,你可以指定另一个标记符。对于特殊标记的子串充作什么标示符,没有任何限制。因此你可以用它来替换执行Python表达式(只要使字典 d__getitem 执行 eval 操作即可)或其它对象。另外,你可以很容易的做到一些有用的效果,比如:

print expand('just "a" ""little"" test', {'a' : 'one', '' : '"'})
输出 just one "little" test.

高级编程用户可以通过继承Python的 string.Template 类来得到上述的这些功能, 但本例中的 expand 小函数已经足够的简单和灵活了

参见

Python库函数参考中关于 string.Template 的内容,序列类型章节(关于字符串方法 splitjoin 以及切片操作的介绍),以及字典章节(关于索引和 get 方法)。关于 string.Template 类的更多说明请参见: Python食谱1.17

Leave a Response