在Python中对正则表达式使用compile有什么好处吗?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
在Python中对正则表达式使用compile有什么好处吗?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
当前回答
我真的很尊重上面所有的答案。在我看来 是的!当然,使用re.compile而不是一次又一次地编译正则表达式是值得的。
使用re.compile可以使代码更加动态,因为您可以调用已经编译好的正则表达式,而不是一次又一次地编译。这对你有好处:
处理器的努力 时间复杂度。 使正则表达式通用。(可以在findall, search, match中使用) 并使您的程序看起来很酷。
例子:
example_string = "The room number of her room is 26A7B."
find_alpha_numeric_string = re.compile(r"\b\w+\b")
在Findall中使用
find_alpha_numeric_string.findall(example_string)
在搜索中使用
find_alpha_numeric_string.search(example_string)
类似地,您可以将它用于:Match和Substitute
其他回答
一般来说,我发现在编译模式时使用标志比内联使用标志更容易(至少更容易记住如何使用),比如re.I。
>>> foo_pat = re.compile('foo',re.I)
>>> foo_pat.findall('some string FoO bar')
['FoO']
vs
>>> re.findall('(?i)foo','some string FoO bar')
['FoO']
使用re.compile()还有一个额外的好处,即使用re.VERBOSE向正则表达式模式添加注释
pattern = '''
hello[ ]world # Some info on my pattern logic. [ ] to recognize space
'''
re.search(pattern, 'hello world', re.VERBOSE)
虽然这不会影响代码的运行速度,但我喜欢这样做,因为这是我注释习惯的一部分。当我想要修改代码时,我完全不喜欢花时间去记住代码背后的逻辑。
易读性/认知负荷偏好
对我来说,主要的收获是我只需要记住和阅读复杂的正则表达式API语法的一种形式——<compiled_pattern>.method(xxx)形式而不是那个和re.func(<pattern>, xxx)形式。
re.compile(<pattern>)是一个额外的样板文件,true。
但是考虑到正则表达式,额外的编译步骤不太可能是认知负荷的主要原因。事实上,对于复杂的模式,您甚至可以通过将声明与随后对其调用的任何regex方法分开来获得清晰性。
我倾向于首先在Regex101这样的网站中调优复杂的模式,甚至在单独的最小测试脚本中调优,然后将它们带入我的代码中,因此将声明与其使用分离也适合我的工作流程。
这是个好问题。你经常看到人们毫无理由地使用re.compile。它降低了可读性。但是可以肯定的是,很多时候需要预编译表达式。就像你在循环中重复使用它一样。
这就像编程的一切(实际上是生活中的一切)。运用常识。
我自己刚试过。对于从字符串中解析数字并对其求和的简单情况,使用编译后的正则表达式对象的速度大约是使用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
'reallyCompiled'方法使用内部接口,绕过缓存。注意,在每个循环迭代中编译的代码只迭代了10,000次,而不是一百万次。