2015-06-06 3 views
3

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

import re 
def get_patterns(string): 
    """ 
    Takes a string and returns found groups 
    of numeric and alphabetic characters. 

    """ 
    re_digits = re.compile("(\d+)") 
    re_alpha = re.compile("(?i)([A-Z]+)") 
    digits = re_digits.findall(string) 
    alpha = re_alpha.findall(string) 
    return digits, alpha 

get_patterns("99 bottles of beer on the wall") 
(['99'], ['bottles', 'of', 'beer', 'on', 'the', 'wall']) 

Теперь предположим, что эта функция будет называться сотни тысяч раз, и что это не такой тривиальный пример. Означает ли это, a) вопрос, выполняется ли компиляция регулярных выражений внутри функции, т. Е. Есть ли стоимость затрат для вызова операции компиляции при каждом вызове функции (или она повторно используется из кеша?), И b) если есть, что бы было рекомендуемый подход для предотвращения этих накладных расходов?

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

re_digits = re.compile("(\d+)") 
re_alpha = re.compile("(?i)([A-Z]+)") 
def get_patterns(string, [re_digits, re_alpha]): 

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

UPDATE: В соответствии с рекомендацией Йенса я запустить быструю проверку синхронизации и делает компиляцию в пределах аргументов функции по умолчанию действительно совсем немного (~ 30%) быстрее:

def get_patterns_defaults(string, 
          re_digits=re.compile("(\d+)"), 
          re_alpha=re.compile("(?i)([A-Z]+)") 
         ): 
    """ 
    Takes a string and returns found groups 
    of numeric and alphabetic characters. 

    """ 
    digits = re_digits.findall(string) 
    alpha = re_alpha.findall(string) 
    return digits, alpha 

from timeit import Timer 
test_string = "99 bottles of beer on the wall" 
t = Timer(lambda: get_patterns(test_string)) 
t2 = Timer(lambda: get_patterns_defaults(test_string)) 
print t.timeit(number=100000) # compiled in function body 
print t2.timeit(number=100000) # compiled in args 
0.629958152771 
0.474529981613 
+0

ли вы имеете в виду, чтобы вернуть кортеж '(цифры , alpha) '? – Jens

+0

Отличный вопрос. – bjfletcher

+0

@Jens бы желаемый тип возврата повлиял на подход? Я просто привел пример с игрушкой, но если это имеет значение, мне было бы интересно узнать! – glarue

ответ

4

Одно из решений заключается в использовании аргументов по умолчанию, поэтому они составляются только один раз:

import re 
def get_patterns(string, re_digits=re.compile("(\d+)"), re_alpha=re.compile("(?i)([A-Z]+)")): 
    """ 
    Takes a string and returns found groups 
    of numeric and alphabetic characters. 

    """ 
    digits = re_digits.findall(string) 
    alpha = re_alpha.findall(string) 
    return digits, alpha 

сейчас вы можете позвонить по этому адресу:

get_patterns(string) 
+1

Это интересный способ иметь статические значения внутри функции. Спасибо за интересный трюк. – bjfletcher

+0

@seb отлично, я думаю, что это покрывает базы, сохраняя шаблоны, связанные с используемой функцией, и избегая глобальных привязок. Я тоже не могу поверить, что не думал об этом. Спасибо! – glarue

+0

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

1

Вы можете использовать Функциональность Python timeit (или here и here) для измерения времени выполнения.

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

import re 

_re_digits = re.compile("(\d+)") 
_re_alpha = re.compile("(?i)([A-Z]+)") 

def get_patterns(string): 
    digits = _re_digits.findall(string) 
    alpha = _re_alpha.findall(string) 
    return (digits, alpha) 
1

Удовлетворительный факт: вы можете установить и получить атрибуты на func в Python, как и любой другой объект. Таким образом, другое решение, которое позволяет избежать глобалам и только компилирует регулярные выражения, как только будет что-то вроде этого:

def get_patterns(string): 
    f = get_patterns 
    return f.digits.findall(string), f.alpha.findall(string) 

get_patterns.digits = re.compile("(\d+)") 
get_patterns.alpha = re.compile("(?i)([A-Z]+)") 

Другим решением было бы использовать замыкание:

def make_finder(*regexps): 
    return lambda s: tuple(r.findall(s) for r in regexps) 

get_patterns = make_finder(re.compile("(\d+)"), re.compile("(?i)([A-Z]+)")) 
Смежные вопросы