Is there any benefit in using compile for regular expressions in Python?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
Is there any benefit in using compile for regular expressions in Python?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
I agree with Honest Abe that the
match(...)
in the given examples are different. They are not a one-to-one comparisons and thus, outcomes are vary. To simplify my reply, I use A, B, C, D for those functions in question. Oh yes, we are dealing with 4 functions inre.py
instead of 3.Running this piece of code:
is same as running this code:
Because, when looked into the source
re.py
, (A + B) means:and (C) is actually:
So, (C) is not the same as (B). In fact, (C) calls (B) after calling (D) which is also called by (A). In other words,
(C) = (A) + (B)
. Therefore, comparing (A + B) inside a loop has same result as (C) inside a loop.George's
regexTest.py
proved this for us.Everyone's interest is, how to get the result of 2.323 seconds. In order to make sure
compile(...)
only get called once, we need to store the compiled regex object in memory. If we are using a class, we could store the object and reuse when every time our function get called.If we are not using class (which is my request today), then I have no comment. I'm still learning to use global variable in Python, and I know global variable is a bad thing.
One more point, I believe that using
(A) + (B)
approach has an upper hand. Here are some facts as I observed (please correct me if I'm wrong):Calls A once, it will do one search in the
_cache
followed by onesre_compile.compile()
to create a regex object. Calls A twice, it will do two searches and one compile (because the regex object is cached).If the
_cache
get flushed in between, then the regex object is released from memory and Python need to compile again. (someone suggest that Python won't recompile.)If we keep the regex object by using (A), the regex object will still get into _cache and get flushed somehow. But our code keep a reference on it and the regex object will not be released from memory. Those, Python need not to compile again.
The 2 seconds differences in George's test compiledInLoop vs compiled is mainly the time required to build the key and search the _cache. It doesn't mean the compile time of regex.
George's reallycompile test show what happen if it really re-do the compile every time: it will be 100x slower (he reduced the loop from 1,000,000 to 10,000).
Here are the only cases that (A + B) is better than (C):
Case that (C) is good enough:
Just a recap, here are the A B C:
Thanks for reading.
FWIW:
so, if you're going to be using the same regex a lot, it may be worth it to do
re.compile
(especially for more complex regexes).The standard arguments against premature optimization apply, but I don't think you really lose much clarity/straightforwardness by using
re.compile
if you suspect that your regexps may become a performance bottleneck.Update:
Under Python 3.6 (I suspect the above timings were done using Python 2.x) and 2018 hardware (MacBook Pro), I now get the following timings:
I also added a case (notice the quotation mark differences between the last two runs) that shows that
re.match(x, ...)
is literally [roughly] equivalent tore.compile(x).match(...)
, i.e. no behind-the-scenes caching of the compiled representation seems to happen.This is a good question. You often see people use re.compile without reason. It lessens readability. But sure there are lots of times when pre-compiling the expression is called for. Like when you use it repeated times in a loop or some such.
It's like everything about programming (everything in life actually). Apply common sense.
Interestingly, compiling does prove more efficient for me (Python 2.5.2 on Win XP):
Running the above code once as is, and once with the two
if
lines commented the other way around, the compiled regex is twice as fastPerformance difference aside, using re.compile and using the compiled regular expression object to do match (whatever regular expression related operations) makes the semantics clearer to Python run-time.
I had some painful experience of debugging some simple code:
and later I'd use compare in
where
patternPhrases
is supposed to be a variable containing regular expression string,x[columnIndex]
is a variable containing string.I had trouble that
patternPhrases
did not match some expected string!But if I used the re.compile form:
then in
Python would have complained that "string does not have attribute of match", as by positional argument mapping in
compare
,x[columnIndex]
is used as regular expression!, when I actually meantIn my case, using re.compile is more explicit of the purpose of regular expression, when it's value is hidden to naked eyes, thus I could get more help from Python run-time checking.
So the moral of my lesson is that when the regular expression is not just literal string, then I should use re.compile to let Python to help me to assert my assumption.
Using the given examples:
The match method in the example above is not the same as the one used below:
re.compile() returns a regular expression object, which means
h
is a regex object.The regex object has its own match method with the optional pos and endpos parameters:
regex.match(string[, pos[, endpos]])
pos
endpos
The regex object's search, findall, and finditer methods also support these parameters.
re.match(pattern, string, flags=0)
does not support them as you can see,nor does its search, findall, and finditer counterparts.
A match object has attributes that complement these parameters:
match.pos
match.endpos
A regex object has two unique, possibly useful, attributes:
regex.groups
regex.groupindex
And finally, a match object has this attribute:
match.re