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

时间:2010-07-29 23:42:02

标签: 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']

2 个答案:

答案 0 :(得分:10)

您给出的具体示例(删除单个字符)非常适合translate字符串方法,单个字符替换为单个字符。如果输入字符串是Unicode,那么,以及上面两种“替换”,使用translate方法替换具有多个字符串的单个字符也是可以的(如果您需要处理但字节字符串。

如果您需要替换多个字符的子字符串,那么我还建议使用正则表达式 - 尽管不是@ gnibbler的回答推荐的方式;相反,我会从r'onestring|another|yetanother|orthis'构建正则表达式(加入你想要用竖线替换的子串 - 当然,如果它们包含特殊字符,也要确保它们re.escape并写一个简单的替代 - 基于词典的功能。

我现在不打算提供很多代码,因为我不知道这两段中的哪一段适用于你的实际需要,但是(当我后来回到家并再次检查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)

使用示例:

r = Replacer(zap='zop', zip='zup')
print r.replace('allazapollezipzapzippopzip')

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

r = Replacer(abc='xyz', def='yyt', ghi='zzq')

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

r = Replacer(abc='xyz', ghi='zzq', **{'def': 'yyt'})

等。

我觉得这对于一个类(而不是程序编程)很有用,因为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')

唯一真正的优势可能是一个非常适度的更好的性能(需要使用timeit检查“基准案例”被认为是重要且代表使用它的应用程序)作为访问“自由变量”(在这种情况下,replacementslocator_doreplace)可能比在正常的基于类的方法中访问限定名称(self.replacements等)要快一些(无论这个方法)是否会依赖于正在使用的Python实现,因此需要在重要的基准测试中使用timeit进行检查!)。

答案 1 :(得分:0)

您可能会发现创建正则表达式并立即执行所有替换更快。

最好将替换代码移到一个函数中,以便您可以记住,如果列表中可能有重复项

>>> import re
>>> [re.sub('[aeiou]','',s) for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']


>>> def replacer(s, memo={}):
...   if s not in memo:
...     memo[s] = re.sub('[aeiou]','',s)
...   return memo[s]
... 
>>> [replacer(s) for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']