Значение a
будет b
, но значением выражение будет c
. То есть в
d = (a = b, c);
a будет равно b
, а d
будет равно c
.
Я имел большой опыт, работающий скомпилированные regex 1000-е времен по сравнению с компиляцией на лету, и не заметил заметного различия. Очевидно, это анекдотично, и конечно не большой аргумент против компиляция, но я нашел, что различие незначительно.
РЕДАКТИРОВАНИЕ: После быстрого взгляда на фактический код библиотеки Python 2.5 я вижу, что Python внутренне компилирует И КЭШИ regexes каждый раз, когда Вы используете их так или иначе (включая вызовы к re.match()
), таким образом, Вы действительно только изменяетесь, КОГДА regex компилируется и не должен экономить много времени вообще - только время, это берет для проверки кэша (ключевой поиск на внутреннем dict
тип).
От модуля re.py (комментарии являются моими):
def match(pattern, string, flags=0):
return _compile(pattern, flags).match(string)
def _compile(*key):
# Does cache check at top of function
cachekey = (type(key[0]),) + key
p = _cache.get(cachekey)
if p is not None: return p
# ...
# Does actual compilation on cache miss
# ...
# Caches compiled regex
if len(_cache) >= _MAXCACHE:
_cache.clear()
_cache[cachekey] = p
return p
я все еще часто предварительно компилирую регулярные выражения, но только связывать их с хорошим, допускающим повторное использование именем, не для любого ожидаемого увеличения производительности.
Мое понимание - то, что те два примера эффективно эквивалентны. Единственная разница - то, что в первом, можно снова использовать скомпилированное регулярное выражение в другом месте, не заставляя его быть скомпилированными снова.
Вот ссылка для Вас: http://diveintopython3.ep.io/refactoring.html
Вызывание поисковой функции объекта скомпилированного шаблона со строкой 'M' выполняет то же самое как звонящий re.search и с регулярным выражением и со строкой 'M'. Только очень, намного быстрее. (На самом деле функция re.search просто компилирует регулярное выражение и называет получающийся метод поиска объекта шаблона для Вас.)
Регулярные выражения компилируются прежде чем быть используемым при использовании второй версии. Если Вы идете в выполнение его много раз, это - definatly лучше для компиляции его сначала. Не компилируя каждый раз Вы соответствуете для одного off's, прекрасен.
FWIW:
$ python -m timeit -s "import re" "re.match('hello', 'hello world')"
100000 loops, best of 3: 3.82 usec per loop
$ python -m timeit -s "import re; h=re.compile('hello')" "h.match('hello world')"
1000000 loops, best of 3: 1.26 usec per loop
так, если Вы собираетесь быть использованием тот же regex много, это может стоить того, чтобы сделать re.compile
(специально для более сложного regexes).
стандартные аргументы против преждевременной оптимизации применяются, но я не думаю, что Вы действительно теряете много ясности/прямоты при помощи re.compile
, если Вы подозреваете, что Ваш regexps может стать узким местом производительности.
Обновление:
В соответствии с Python 3.6 (я подозреваю, вышеупомянутые синхронизации были сделаны с помощью Python 2.x), и аппаратные средства 2018 (MacBook Pro), я теперь получаю следующие синхронизации:
% python -m timeit -s "import re" "re.match('hello', 'hello world')"
1000000 loops, best of 3: 0.661 usec per loop
% python -m timeit -s "import re; h=re.compile('hello')" "h.match('hello world')"
1000000 loops, best of 3: 0.285 usec per loop
% python -m timeit -s "import re" "h=re.compile('hello'); h.match('hello world')"
1000000 loops, best of 3: 0.65 usec per loop
% python --version
Python 3.6.5 :: Anaconda, Inc.
я также добавил, случай (заметьте различия в кавычке между последними двумя выполнениями), который показывает, что re.match(x, ...)
буквально [примерно] эквивалентно re.compile(x).match(...)
, т.е. никакого закулисного кэширования скомпилированного представления, кажется, не происходит.
Интересно, компиляция действительно оказывается более эффективной для меня (Python 2.5.2 на XP Победы):
import re
import time
rgx = re.compile('(\w+)\s+[0-9_]?\s+\w*')
str = "average 2 never"
a = 0
t = time.time()
for i in xrange(1000000):
if re.match('(\w+)\s+[0-9_]?\s+\w*', str):
#~ if rgx.match(str):
a += 1
print time.time() - t
Выполнение вышеупомянутый код однажды, как, и однажды с два if
строки, прокомментированные наоборот, скомпилированный regex вдвое более быстр
Для меня самое большое преимущество для re.compile
является способностью разделить определение regex от его использования.
Даже простое выражение такой как 0|[1-9][0-9]*
(целое число в основе 10 без начальных нулей) может быть достаточно сложным, который Вы не должны перепечатывать его, проверить, сделали ли Вы какие-либо опечатки, и позже должны перепроверить, существуют ли опечатки, когда Вы начинаете отлаживать. Плюс, более хорошо использовать имя переменной, такое как цифра или num_b10, чем 0|[1-9][0-9]*
.
, конечно, возможно сохранить строки и передать их re.match; однако, это меньше читаемо:
num = "..."
# then, much later:
m = re.match(num, input)
По сравнению с компиляцией:
num = re.compile("...")
# then, much later:
m = num.match(input)
, Хотя это довольно близко, последняя строка вторых чувств, более естественных и более простых, когда используется неоднократно.
Это - хороший вопрос. Вы часто видите, что люди используют re.compile без причины. Это уменьшает удобочитаемость. Но уверенный существует много времен, когда предварительная компиляция выражения требуется. Как то, когда Вы используете повторенные времена в цикле или некоторых такой.
Это похоже на все о программировании (все в жизни на самом деле). Примените здравый смысл.
В целом я нахожу, что легче использовать флаги (по крайней мере, легче помнить как), как re.I
при компиляции шаблонов, чем использовать флаги встраивают.
>>> foo_pat = re.compile('foo',re.I)
>>> foo_pat.findall('some string FoO bar')
['FoO']
по сравнению с
>>> re.findall('(?i)foo','some string FoO bar')
['FoO']
(месяцы спустя) легко добавить свой собственный кеш вокруг re.match, или что-нибудь еще в этом отношении -
""" Re.py: Re.match = re.match + cache
efficiency: re.py does this already (but what's _MAXCACHE ?)
readability, inline / separate: matter of taste
"""
import re
cache = {}
_re_type = type( re.compile( "" ))
def match( pattern, str, *opt ):
""" Re.match = re.match + cache re.compile( pattern )
"""
if type(pattern) == _re_type:
cpat = pattern
elif pattern in cache:
cpat = cache[pattern]
else:
cpat = cache[pattern] = re.compile( pattern, *opt )
return cpat.match( str )
# def search ...
Вибни, было бы неплохо, если бы: cachehint (size =), cacheinfo () -> size, hits, nclear ...
Я провел этот тест, прежде чем наткнуться на обсуждение здесь. Однако, проведя его, я подумал, что, по крайней мере, опубликую свои результаты.
Я украл и убрал пример из "Овладения регулярными выражениями" Джеффа Фридла. Это на Macbook под управлением OSX 10.6 (2 ГГц intel core 2 duo, 4 ГБ барабан). Версия на питоне 2.6.1.
Run 1 - используя re.compile
import re
import time
import fpformat
Regex1 = re.compile('^(a|b|c|d|e|f|g)+$')
Regex2 = re.compile('^[a-g]+$')
TimesToDo = 1000
TestString = ""
for i in range(1000):
TestString += "abababdedfg"
StartTime = time.time()
for i in range(TimesToDo):
Regex1.search(TestString)
Seconds = time.time() - StartTime
print "Alternation takes " + fpformat.fix(Seconds,3) + " seconds"
StartTime = time.time()
for i in range(TimesToDo):
Regex2.search(TestString)
Seconds = time.time() - StartTime
print "Character Class takes " + fpformat.fix(Seconds,3) + " seconds"
Alternation takes 2.299 seconds
Character Class takes 0.107 seconds
Run 2 - не используя re.compile
import re
import time
import fpformat
TimesToDo = 1000
TestString = ""
for i in range(1000):
TestString += "abababdedfg"
StartTime = time.time()
for i in range(TimesToDo):
re.search('^(a|b|c|d|e|f|g)+$',TestString)
Seconds = time.time() - StartTime
print "Alternation takes " + fpformat.fix(Seconds,3) + " seconds"
StartTime = time.time()
for i in range(TimesToDo):
re.search('^[a-g]+$',TestString)
Seconds = time.time() - StartTime
print "Character Class takes " + fpformat.fix(Seconds,3) + " seconds"
Alternation takes 2.508 seconds
Character Class takes 0.109 seconds
Я просто пробовал это сам. В простом случае выделения числа из строки и его суммирования использование скомпилированного объекта регулярного выражения примерно в два раза быстрее, чем использование методов re
.
Как указывали другие, методы re
(включая re.compile
) ищут строку регулярного выражения в кэше ранее скомпилированных выражений. Следовательно, в обычном случае дополнительные затраты на использование методов re
- это просто затраты на поиск в кэше.
Однако проверка кода показывает, что кэш ограничен 100 выражениями. Напрашивается вопрос, насколько больно переполнять кеш? Код содержит внутренний интерфейс для компилятора регулярных выражений, re.sre_compile.compile
. Если мы вызываем это, мы обходим кеш. Оказывается, это примерно на два порядка медленнее для базового регулярного выражения, такого как r '\ w + \ s + ([0-9 _] +) \ s + \ w *'
.
Вот мой тест:
#!/usr/bin/env python
import re
import time
def timed(func):
def wrapper(*args):
t = time.time()
result = func(*args)
t = time.time() - t
print '%s took %.3f seconds.' % (func.func_name, t)
return result
return wrapper
regularExpression = r'\w+\s+([0-9_]+)\s+\w*'
testString = "average 2 never"
@timed
def noncompiled():
a = 0
for x in xrange(1000000):
m = re.match(regularExpression, testString)
a += int(m.group(1))
return a
@timed
def compiled():
a = 0
rgx = re.compile(regularExpression)
for x in xrange(1000000):
m = rgx.match(testString)
a += int(m.group(1))
return a
@timed
def reallyCompiled():
a = 0
rgx = re.sre_compile.compile(regularExpression)
for x in xrange(1000000):
m = rgx.match(testString)
a += int(m.group(1))
return a
@timed
def compiledInLoop():
a = 0
for x in xrange(1000000):
rgx = re.compile(regularExpression)
m = rgx.match(testString)
a += int(m.group(1))
return a
@timed
def reallyCompiledInLoop():
a = 0
for x in xrange(10000):
rgx = re.sre_compile.compile(regularExpression)
m = rgx.match(testString)
a += int(m.group(1))
return a
r1 = noncompiled()
r2 = compiled()
r3 = reallyCompiled()
r4 = compiledInLoop()
r5 = reallyCompiledInLoop()
print "r1 = ", r1
print "r2 = ", r2
print "r3 = ", r3
print "r4 = ", r4
print "r5 = ", r5
</pre>
And here is the output on my machine:
<pre>
$ regexTest.py
noncompiled took 4.555 seconds.
compiled took 2.323 seconds.
reallyCompiled took 2.325 seconds.
compiledInLoop took 4.620 seconds.
reallyCompiledInLoop took 4.074 seconds.
r1 = 2000000
r2 = 2000000
r3 = 2000000
r4 = 2000000
r5 = 20000
Методы «действительно компилированного» используют внутренний интерфейс, который обходит кеш. Обратите внимание, что тот, который компилируется на каждой итерации цикла, повторяется только 10 000 раз, а не один миллион.
OnAfterInstall
? Если так, как Вы вставляете Сценарий там? – jp2code 27 July 2011 в 15:32