Pythonic и эффективный способ определения нескольких регулярных выражений для использования во многих итерациях

В настоящее время я пишу скрипт Python для обработки около 10 000 входных документов. Основываясь на результатах выполнения сценария, я замечаю, что первые 400+ документов обрабатываются очень быстро, а затем скрипт замедляется, хотя входные документы имеют примерно одинаковый размер.

Я предполагаю, что это может быть связано с тем фактом, что большая часть обработки документов выполняется с помощью регулярных выражений, которые я не сохраняю как объекты регулярных выражений после их компиляции. Вместо этого я перекомпиляю регулярные выражения, когда мне это нужно.

Поскольку мой скрипт имеет около 10 различных функций, все из которых используют около 10-20 различных шаблонов регулярных выражений, мне интересно, что было бы более эффективным способом в Python, чтобы избежать повторной компиляции шаблонов регулярных выражений снова и снова (в Perl я мог просто включить модификатор //o ).

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

 pattern = re.compile() 

результирующий объект regex не будет сохранен до следующего вызова функции для следующей итерации (каждая функция вызывается, но один раз для документа).

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

Какой-нибудь совет здесь, как справиться с этим аккуратно и эффективно?

Модуль re кэширует скомпилированные шаблоны регулярных выражений. Кэш очищается, когда он достигает размера re._MAXCACHE, который по умолчанию равен 100. (Поскольку у вас есть 10 функций с 10-20 регулярными выражениями каждый (т.е. 100-200 регулярных выражений), ваше замедленное замедление имеет смысл с очисткой кеш.)

Если у вас все в порядке с изменением личных переменных, быстрое и грязное исправление для вашей программы может состоять в том, чтобы установить re._MAXCACHE на большее значение:

 import re re._MAXCACHE = 1000 

В прошлый раз, когда я смотрел, re.compile поддерживал довольно небольшой кеш, и когда он заполнился, просто опустошил его. Сделай сам без ограничений:

 class MyRECache(object): def __init__(self): self.cache = {} def compile(self, regex_string): if regex_string not in self.cache: self.cache[regex_string] = re.compile(regex_string) return self.cache[regex_string] 

Скомпилированное регулярное выражение автоматически кэшируется re.compile , re.search и re.match , но максимальный размер кеша составляет 100 в Python 2.7, поэтому вы переполняете кеш.

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

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

В духе «простого лучше» я бы использовал небольшую вспомогательную функцию:

 def rc(pattern, flags=0): key = pattern, flags if key not in rc.cache: rc.cache[key] = re.compile(pattern, flags) return rc.cache[key] rc.cache = {} 

Применение:

 rc('[az]').sub... rc('[az]').findall <- no compilation here к rc('[az]').sub... rc('[az]').findall <- no compilation here 

Я также рекомендую вам попробовать регулярное выражение . Среди многих других преимуществ по сравнению с запасом, его MAXCACHE по умолчанию 500, и он не будет полностью потерян при переполнении.