Эффективно выполните несколько строковых замен в Python

Если я хотел бы выполнить несколько строковых замен, что самый эффективный путь состоит в том, чтобы выполнить это?

Пример вида ситуации, с которой я встретился в своих перемещениях, следующие:

>>> strings = ['a', 'list', 'of', 'strings']
>>> [s.replace('a', '')...replace('u', '')  for s in strings if len(s) > 2]
['a', 'lst', 'of', 'strngs']
8
задан Tim McNamara 5 August 2010 в 02:14
поделиться

2 ответа

Приведенный вами конкретный пример (удаление одиночных символов) идеально подходит для метода строк translate , как и замена одиночных символов одиночными символами. Если входная строка является строкой Unicode, то, наряду с двумя вышеуказанными видами «подстановки», замена одиночных символов на несколько символьных строк также подходит для метода translate (не если вам нужно работать с байтовыми строками).

Если вам нужно заменить подстроки из нескольких символов, я бы также рекомендовал использовать регулярное выражение - хотя и не так, как рекомендует ответ @gnibbler; я бы скорее построил регулярное выражение из r'onestring | another | elseanother | orthis ' (присоедините подстроки, которые вы хотите заменить, вертикальными полосами - обязательно также re.escape ] их, если они, конечно, содержат специальные символы) и напишите простую функцию-подстановку на основе dict.

Я не собираюсь предлагать много кода в настоящее время, так как я не знаю, какой из двух абзацев относится к вашим реальным потребностям, но (когда я позже вернусь домой и снова проверю ТАК ;-) я Буду рад отредактировать, чтобы добавить пример кода по мере необходимости, в зависимости от ваших правок к вашему вопросу (более полезно, чем комментарии к этому ответу ;-).

Правка : в комментарии OP говорит, что ему нужен «более общий» ответ (не уточняя, что это значит), затем в редактировании своего Q он говорит, что хочет изучить «компромиссы» между различными фрагментами , все из которых используют односимвольные подстроки (и проверяют их наличие, а не заменяют, как было первоначально запрошено - совершенно другая семантика, конечно).

Учитывая эту полную и полную путаницу, все, что я могу сказать, это то, что для «проверки компромиссов» (с точки зрения производительности) мне нравится использовать python -mtimeit -s'setup things here '' операторы для проверки ' (убедитесь, что проверяемые операторы не имеют побочных эффектов, чтобы избежать искажения измерений времени, поскольку timeit неявно зацикливается для обеспечения точных измерений времени).

Общий ответ (без каких-либо компромиссов и с участием многосимвольных подстрок, что полностью противоречит его редактированию Q, но согласуется с его комментариями - эти два полностью противоречат друг другу, что , конечно, невозможно встретить both):

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 в «тестовых примерах», считающихся значительными и репрезентативными для приложения, использующего его) в качестве доступа к «свободные переменные» ( замены , локатор , _doreplace ) в этом случае могут быть немного быстрее, чем доступ к полным именам ( self.replacements и т. Д.) В обычном, основанном на классах подходе (так ли это, будет зависеть от используемой реализации Python, откуда необходимость проверять с помощью timeit на важных тестах!).

12
ответ дан 5 December 2019 в 13:59
поделиться

Вы можете обнаружить, что быстрее создать регулярное выражение и выполнить все замены сразу.

Также неплохо перенести код замены в функцию, чтобы вы могли запоминать, если у вас есть вероятность иметь дубликаты в списке

>>> 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']
0
ответ дан 5 December 2019 в 13:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: