在Python中有效地执行多个字符串替换

Tim*_*ara 8 python string immutability

如果我想进行多个字符串替换,那么最有效的方法是什么呢?

我旅行中遇到的一种情况如下:

>>> strings = ['a', 'list', 'of', 'strings']
>>> [s.replace('a', '')...replace('u', '')  for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 12

您给出的具体示例(删除单个字符)非常适合translate字符串方法,单个字符替换单个字符也是如此.如果输入字符串是Unicode,那么,以及上面两种"替换",用该translate方法替换具有多个字符串的单个字符也是好的(不过如果你需要处理字节字符串) .

如果你需要替换多个字符的子串,那么我还建议使用正则表达式 - 尽管不是@ gnibbler的回答推荐的方式; 相反,我建立正则表达式r'onestring|another|yetanother|orthis'(加入你想要用竖线替换的子串 - 当然,re.escape如果它们包含特殊字符,也要确保它们也是如此)并根据dict编写一个简单的替换函数.

我不会在这个时候提供很多代码,因为我不知道这两段中的哪一段适用于你的实际需要,但是(当我后来回到家并再次检查SO时;-)我将成为很高兴编辑以根据您对问题的编辑添加代码示例(比对此答案的评论更有用;-).

编辑:在评论中,OP说他想要一个"更一般"的答案(没有说明这意味着什么)然后在编辑他的Q时他说他想研究各种片段之间的"权衡",所有片段使用单字符子串(并检查其存在,而不是按原始请求替换 - 当然是完全不同的语义).

考虑到这种完全混乱,我可以说是"检查权衡"(性能方面)我喜欢使用python -mtimeit -s'setup things here' 'statements to check'(确保要检查的语句没有副作用以避免扭曲时间测量,因为timeit隐式循环提供准确定时测量).

一般的答案(没有任何折衷,并且涉及多个字符的字符串,这样完全违背自己的Q的编辑,但辅音他的意见-这两者是完全矛盾的这当然不可能同时满足):

import re

class Replacer(object):

  def __init__(self, **replacements):
    self.replacements = replacements
    self.locator = re.compile('|'.join(re.escape(s) for s in replacements))

  def _doreplace(self, mo):
    return self.replacements[mo.group()]

  def replace(self, s):
    return self.locator.sub(self._doreplace, s)
Run Code Online (Sandbox Code Playgroud)

使用示例:

r = Replacer(zap='zop', zip='zup')
print r.replace('allazapollezipzapzippopzip')
Run Code Online (Sandbox Code Playgroud)

如果要替换的某些子字符串是Python关键字,则需要更少地直接传递它们,例如,以下内容:

r = Replacer(abc='xyz', def='yyt', ghi='zzq')
Run Code Online (Sandbox Code Playgroud)

会失败因为def是关键字,所以你需要例如:

r = Replacer(abc='xyz', ghi='zzq', **{'def': 'yyt'})
Run Code Online (Sandbox Code Playgroud)

等等.

我发现这对于一个类(而不是程序编程)来说是一个很好的用途,因为RE要定位要替换的子串,表示要替换它们的dict,以及执行替换的方法,真的喊出来"保持一致" ",并且类实例是在Python中执行这种"保持在一起"的正确方法.一个闭包工厂也可以工作(因为该replace方法实际上是实例中唯一需要在"外部"可见的部分),但是可能不那么清晰,难以调试:

def make_replacer(**replacements):
  locator = re.compile('|'.join(re.escape(s) for s in replacements))

  def _doreplace(mo):
    return replacements[mo.group()]

  def replace(s):
    return locator.sub(_doreplace, s)

  return replace

r = make_replacer(zap='zop', zip='zup')
print r('allazapollezipzapzippopzip')
Run Code Online (Sandbox Code Playgroud)

唯一真正的优势可能是一个很谦虚更好的性能(需要与检查timeit的"标杆案例"考虑显著和代表使用它的应用程序)为获得"自由变量"( ,,replacements 在这种情况下)可能是比正常的,基于类的方法访问限定名称(等)要快得多(这是否是这种情况将取决于使用的Python实现,需要检查重要的基准测试!).locator_doreplaceself.replacementstimeit