Pythonで正規表現にコンパイルを使用することに何か利点はありますか?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
私はコンパイル済みの正規表現を何千回も実行するのに対し、オンザフライでコンパイルすることに多くの経験を積んできましたが、認識できる違いに気付いたことはありません。明らかに、これは逸話的であり、確かに素晴らしい議論ではありませんに対してコンパイルしていますが、違いは無視できるほどです。
編集:実際のPython 2.5ライブラリコードをちょっと見ただけで、Pythonは正規表現を使用するたびに内部的にコンパイルされ、キャッシュされます(re.match()
への呼び出しも含む)。キャッシュをチェックするのにかかる時間(内部の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
私はいまだに正規表現をプリコンパイルすることが多いのですが、期待されるパフォーマンス向上のためではなく、それらをNiceの再利用可能な名前にバインドするためだけに使用しています。
私にとってre.compile
の最大の利点は、正規表現の定義をその使用から切り離すことができることです。
0|[1-9][0-9]*
(先行ゼロなしの基数10の整数)のような単純な式でも複雑なので、入力し直す必要はありません。タイプミスがあるかどうかを確認し、後で開始時にタイプミスがあるかどうかを確認する必要がありますデバッグ加えて、numや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)
かなり似ていますが、2番目の最後の行は繰り返し使用するとより自然でシンプルになります。
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
したがって、同じ正規表現を頻繁に使用する場合は、(特に複雑な正規表現の場合)re.compile
を実行する価値があります。
時期尚早の最適化に対する標準的な議論が適用されますが、あなたの正規表現がパフォーマンスのボトルネックになるかもしれないと疑うならば、re.compile
を使うことによってあなたがあまり明確さ/単純さを失うとは思わない。
更新:
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(...)
]と同等であること、つまりコンパイルされた表現のバックグラウンドキャッシングが起こらないことを示すケース(最後の2つの実行の間の引用符の違いに注意)を追加しました。
これは簡単なテストケースです。
~$ for x in 1 10 100 1000 10000 100000 1000000; do python -m timeit -n $x -s 'import re' 're.match("[0-9]{3}-[0-9]{3}-[0-9]{4}", "123-123-1234")'; done
1 loops, best of 3: 3.1 usec per loop
10 loops, best of 3: 2.41 usec per loop
100 loops, best of 3: 2.24 usec per loop
1000 loops, best of 3: 2.21 usec per loop
10000 loops, best of 3: 2.23 usec per loop
100000 loops, best of 3: 2.24 usec per loop
1000000 loops, best of 3: 2.31 usec per loop
re.compileで:
~$ for x in 1 10 100 1000 10000 100000 1000000; do python -m timeit -n $x -s 'import re' 'r = re.compile("[0-9]{3}-[0-9]{3}-[0-9]{4}")' 'r.match("123-123-1234")'; done
1 loops, best of 3: 1.91 usec per loop
10 loops, best of 3: 0.691 usec per loop
100 loops, best of 3: 0.701 usec per loop
1000 loops, best of 3: 0.684 usec per loop
10000 loops, best of 3: 0.682 usec per loop
100000 loops, best of 3: 0.694 usec per loop
1000000 loops, best of 3: 0.702 usec per loop
ですから、この単純なケースではコンパイルが速いようですたとえ一度だけマッチしたとしても。
私はこれを自分で試しただけです。文字列から数値を解析して合計するという単純なケースでは、コンパイル済みの正規表現オブジェクトを使用するほうがre
メソッドを使用するよりも約2倍高速です。
他の人が指摘したように、re
メソッド(re.compile
を含む)は以前にコンパイルされた式のキャッシュで正規表現文字列を検索します。したがって、通常の場合、re
メソッドを使用するための追加コストは、単にキャッシュルックアップのコストです。
ただし、 code を調べると、キャッシュは100式に制限されていることがわかります。これは疑問を投げかける、それがキャッシュをオーバーフローさせることがどれほど痛いのか?コードには、正規表現コンパイラへの内部インターフェイスre.sre_compile.compile
が含まれています。それを呼び出すと、キャッシュをバイパスします。 r'\w+\s+([0-9_]+)\s+\w*'
のような基本的な正規表現では、それはおよそ2桁遅くなります。
これが私のテストです:
#!/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'メソッドはキャッシュをバイパスする内部インタフェースを使用します。各ループ反復でコンパイルされるものは、100万回ではなく、10,000回だけ反復されることに注意してください。
与えられた例の中のmatch(...)
は異なるという点で、私は誠実な安倍に同意します。それらは1対1の比較ではないため、結果はさまざまです。私の返事を簡単にするために、私は問題のそれらの機能のためにA、B、C、Dを使います。ああ、私たちは3の代わりにre.py
の4つの関数を扱っています。
このコードを実行する:
h = re.compile('hello') # (A)
h.match('hello world') # (B)
このコードを実行するのと同じです。
re.match('hello', 'hello world') # (C)
なぜなら、ソースre.py
を見ると、(A + B)は
h = re._compile('hello') # (D)
h.match('hello world')
そして(C)は実際には
re._compile('hello').match('hello world')
したがって、(C)は(B)と同じではありません。実際、(C)は(D)を呼び出した後に(B)を呼び出します。これは(A)からも呼び出されます。つまり(C) = (A) + (B)
です。したがって、ループ内で(A + B)を比較すると、ループ内で(C)と同じ結果になります。
ジョージのregexTest.py
はこれを証明してくれました。
noncompiled took 4.555 seconds. # (C) in a loop
compiledInLoop took 4.620 seconds. # (A + B) in a loop
compiled took 2.323 seconds. # (A) once + (B) in a loop
みんなの興味は、どうやって2.323秒の結果を得るかです。 compile(...)
が一度だけ呼び出されるようにするために、コンパイルされた正規表現オブジェクトをメモリに保存する必要があります。クラスを使用している場合は、オブジェクトを格納して、関数が呼び出されるたびに再利用することができます。
class Foo:
regex = re.compile('hello')
def my_function(text)
return regex.match(text)
クラスを使用していない場合(これが今日の私の要求です)、私はコメントがありません。私はまだPythonでグローバル変数を使うことを学んでいます、そしてグローバル変数は悪いことであることを私は知っています。
もう1点、(A) + (B)
アプローチを使用することは優位性があると思います。私が観察したように、ここにいくつかの事実があります(私が間違っているなら私を訂正してください):
一度Aを呼び出すと、正規表現オブジェクトを作成するために_cache
を1回検索し、続いてsre_compile.compile()
を1回検索します。 Aを2回呼び出すと、2回の検索と1回のコンパイルが行われます(正規表現オブジェクトがキャッシュされるため)。
_cache
がその間にフラッシュされると、正規表現オブジェクトはメモリから解放され、Pythonは再度コンパイルする必要があります。 (Pythonは再コンパイルしないと誰かが提案しています。)
(A)を使用して正規表現オブジェクトを保持している場合、正規表現オブジェクトは依然として_cacheに入り、何らかの形でフラッシュされます。しかし、私たちのコードはそれを参照し続け、正規表現オブジェクトはメモリから解放されません。それらは、Pythonは再度コンパイルする必要はありません。
GeorgeのテストcompileInLoopとコンパイルされた2秒の違いは、主にキーの作成と_cacheの検索に必要な時間です。正規表現のコンパイル時間という意味ではありません。
Georgeの本当にコンパイルされたテストは、毎回本当にコンパイルをやり直すとどうなるかを示しています。それは100倍遅くなります(彼はループを1,000,000から10,000に減らしました)。
これが(A + B)が(C)より優れている唯一のケースです。
(C)が十分に良いケース:
要約すると、これがA B Cです。
h = re.compile('hello') # (A)
h.match('hello world') # (B)
re.match('hello', 'hello world') # (C)
読んでくれてありがとう。
ほとんどの場合、re.compileを使用するかどうかにはほとんど違いがありません。内部的には、すべての関数はコンパイルステップによって実装されています。
def match(pattern, string, flags=0):
return _compile(pattern, flags).match(string)
def fullmatch(pattern, string, flags=0):
return _compile(pattern, flags).fullmatch(string)
def search(pattern, string, flags=0):
return _compile(pattern, flags).search(string)
def sub(pattern, repl, string, count=0, flags=0):
return _compile(pattern, flags).sub(repl, string, count)
def subn(pattern, repl, string, count=0, flags=0):
return _compile(pattern, flags).subn(repl, string, count)
def split(pattern, string, maxsplit=0, flags=0):
return _compile(pattern, flags).split(string, maxsplit)
def findall(pattern, string, flags=0):
return _compile(pattern, flags).findall(string)
def finditer(pattern, string, flags=0):
return _compile(pattern, flags).finditer(string)
さらに、re.compile()は追加の間接参照およびキャッシュロジックをバイパスします。
_cache = {}
_pattern_type = type(sre_compile.compile("", 0))
_MAXCACHE = 512
def _compile(pattern, flags):
# internal: compile pattern
try:
p, loc = _cache[type(pattern), pattern, flags]
if loc is None or loc == _locale.setlocale(_locale.LC_CTYPE):
return p
except KeyError:
pass
if isinstance(pattern, _pattern_type):
if flags:
raise ValueError(
"cannot process flags argument with a compiled pattern")
return pattern
if not sre_compile.isstring(pattern):
raise TypeError("first argument must be string or compiled pattern")
p = sre_compile.compile(pattern, flags)
if not (flags & DEBUG):
if len(_cache) >= _MAXCACHE:
_cache.clear()
if p.flags & LOCALE:
if not _locale:
return p
loc = _locale.setlocale(_locale.LC_CTYPE)
else:
loc = None
_cache[type(pattern), pattern, flags] = p, loc
return p
re.compileを使用することによるわずかな速度の利点に加えて、潜在的に複雑なパターン仕様に名前を付け、適用されるビジネスロジックからそれらを分離することから来る読みやすさも好きです。
#### Patterns ############################################################
number_pattern = re.compile(r'\d+(\.\d*)?') # Integer or decimal number
assign_pattern = re.compile(r':=') # Assignment operator
identifier_pattern = re.compile(r'[A-Za-z]+') # Identifiers
whitespace_pattern = re.compile(r'[\t ]+') # Spaces and tabs
#### Applications ########################################################
if whitespace_pattern.match(s): business_logic_rule_1()
if assign_pattern.match(s): business_logic_rule_2()
他の1人の回答者はpycファイルがコンパイルされたパターンを直接格納していると誤って信じていました。ただし、実際には、PYCがロードされるたびに再構築されます。
>>> from dis import dis
>>> with open('tmp.pyc', 'rb') as f:
f.read(8)
dis(marshal.load(f))
1 0 LOAD_CONST 0 (-1)
3 LOAD_CONST 1 (None)
6 IMPORT_NAME 0 (re)
9 STORE_NAME 0 (re)
3 12 LOAD_NAME 0 (re)
15 LOAD_ATTR 1 (compile)
18 LOAD_CONST 2 ('[aeiou]{2,5}')
21 CALL_FUNCTION 1
24 STORE_NAME 2 (lc_vowels)
27 LOAD_CONST 1 (None)
30 RETURN_VALUE
上記の逆アセンブリは、次のものを含むtmp.py
のPYCファイルに由来します。
import re
lc_vowels = re.compile(r'[aeiou]{2,5}')
一般的に、私はフラグをインラインで使うよりもパターンをコンパイルするときの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']
与えられた例を使う:
h = re.compile('hello')
h.match('hello world')
上記の例のmatchメソッドは、以下で使用されているものとは異なります。
re.match('hello', 'hello world')
re.compile() は 正規表現オブジェクト を返します。これはh
が正規表現オブジェクトであることを意味します。
正規表現オブジェクトは独自の match メソッドとオプションのposとを持ちます)endposパラメータ
regex.match(string[, pos[, endpos]])
pos
オプションの2番目のパラメータposは、検索を開始する文字列のインデックスを指定します。デフォルトは0です。これは文字列をスライスすることと完全に同等ではありません。
'^'
パターン文字は、文字列の実際の先頭と改行の直後の位置で一致しますが、検索が開始されるインデックスでは必ずしも一致しません。
endpos
オプションのパラメータendposは、文字列の検索範囲を制限します。文字列がendpos文字の長さであるかのようになります。したがって、posから
endpos - 1
までの文字のみが一致するものとして検索されます。 。 endposがposより小さい場合、一致は見つかりません。それ以外の場合、rxがコンパイル済みの正規表現オブジェクトである場合、rx.search(string, 0, 50)
はrx.search(string[:50], 0)
と同等です。
正規表現オブジェクトの検索、findall、およびfinditerメソッドもこれらのパラメータをサポートします。 。
re.match(pattern, string, flags=0)
あなたが見ることができるようにそれらをサポートしていません、
また、検索、findall、およびfinditerも行いません。カウンターパート。
A match object はこれらのパラメータを補完する属性を持ちます。
match.pos
正規表現オブジェクトのsearch()またはmatch()メソッドに渡されたposの値。これは、REエンジンが一致を探し始める文字列へのインデックスです。
match.endpos
正規表現オブジェクトのsearch()またはmatch()メソッドに渡されたendposの値。これはREエンジンが行かない文字列へのインデックスです。
A 正規表現オブジェクト は二つのユニークな、おそらく有用な属性を持っています:
regex.groups
パターン内のキャプチャグループの数。
regex.groupindex
(?P)で定義されたシンボリックグループ名をグループ番号にマッピングする辞書。パターンに記号グループが使用されていない場合、辞書は空です。
そして最後に、 match object はこの属性を持ちます。
match.re
Match()またはsearch()メソッドがこの一致インスタンスを生成した正規表現オブジェクト。
Re.VERBOSEを使用して私の正規表現パターンにコメントを追加するという形で、re.compile()を使用することによる追加の特典が1つあります。
pattern = '''
hello[ ]world # Some info on my pattern logic. [ ] to recognize space
'''
re.search(pattern, 'hello world', re.VERBOSE)
これはコードの実行速度には影響しませんが、これは私のコメント習慣の一部なので、このようにするのが好きです。私は、修正を加えたいときに2か月後に自分のコードの背後にあるロジックを思い出そうとして時間を費やすのが嫌いです。
興味深いことに、コンパイルは私にとってより効率的です(Win XP上のPython 2.5.2):
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
上記のコードをそのまま1回実行し、2つのif
行を使用して反対の方法でコメント化すると、コンパイルされた正規表現は2倍高速になります。
この答えは遅れて到着するかもしれませんが、面白い発見です。正規表現を複数回使用することを計画している場合は、compileを使用すると時間を大幅に節約できます(これはドキュメントにも記載されています)。以下では、コンパイル済み正規表現を使用するのが、matchメソッドが直接呼び出されたときに最も速いことがわかります。コンパイルされた正規表現をre.matchに渡すことはそれをさらに遅くします、そして、パターン文字列と共にre.matchを渡すことは途中でどこかにあります。
>>> ipr = r'\D+((([0-2][0-5]?[0-5]?)\.){3}([0-2][0-5]?[0-5]?))\D+'
>>> average(*timeit.repeat("re.match(ipr, 'abcd100.10.255.255 ')", globals={'ipr': ipr, 're': re}))
1.5077415757028423
>>> ipr = re.compile(ipr)
>>> average(*timeit.repeat("re.match(ipr, 'abcd100.10.255.255 ')", globals={'ipr': ipr, 're': re}))
1.8324008992184038
>>> average(*timeit.repeat("ipr.match('abcd100.10.255.255 ')", globals={'ipr': ipr, 're': re}))
0.9187896518778871
ここでの議論につまずく前に、私はこのテストを実行しました。しかし、それを実行したので、私は少なくとも自分の結果を投稿したいと思った。
私はJeff Friedlの「Mastering Regular Expressions」の例を盗んで規格化しました。これはOSX 10.6(2GHz Intel Core 2 Duo、4GB RAM)を実行しているMacBookにあります。 Pythonのバージョンは2.6.1です。
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
実行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.compileを使用し、(正規表現に関連する演算を問わず)コンパイルに正規表現オブジェクトを使用することで、セマンティクスがPythonランタイムに明確になります。
私はいくつかの簡単なコードをデバッグするのに苦痛な経験をしました:
compare = lambda s, p: re.match(p, s)
そして後で私はcompare inを使うでしょう
[x for x in data if compare(patternPhrases, x[columnIndex])]
ここで、patternPhrases
は正規表現文字列を含む変数であると想定され、x[columnIndex]
は文字列を含む変数です。
patternPhrases
が予期される文字列と一致しないという問題がありました。
しかし、私がre.compileフォームを使ったならば:
compare = lambda s, p: p.match(s)
それから
[x for x in data if compare(patternPhrases, x[columnIndex])]
compare
の位置引数マッピングでは、x[columnIndex]
が正規表現として使用されているので、Pythonは「文字列には一致の属性がありません」と不平を言うでしょう。
compare = lambda p, s: p.match(s)
私の場合、re.compileを使用することは、その値が裸眼で隠されている場合に、正規表現の目的をより明確に示します。そのため、Pythonランタイムチェックからより多くの助けを得ることができます。
したがって、私のレッスンの教訓は、正規表現が単なるリテラル文字列ではない場合は、re.compileを使用して、Pythonが私の仮定を主張できるようにすることです。
パフォーマンス以外にも。
compile
を使用すると、の概念を区別するのに役立ちます。
1.モジュール(re)、
2。正規表現オブジェクト
。試合対象
正規表現の学習を始めたとき
#regex object
regex_object = re.compile(r'[a-zA-Z]+')
#match object
match_object = regex_object.search('1.Hello')
#matching content
match_object.group()
output:
Out[60]: 'Hello'
V.S.
re.search(r'[a-zA-Z]+','1.Hello').group()
Out[61]: 'Hello'
補足として、私はあなたの参照用にモジュールre
の徹底的なチートシートを作りました。
regex = {
'brackets':{'single_character': ['[]', '.', {'negate':'^'}],
'capturing_group' : ['()','(?:)', '(?!)' '|', '\\', 'backreferences and named group'],
'repetition' : ['{}', '*?', '+?', '??', 'greedy v.s. lazy ?']},
'lookaround' :{'lookahead' : ['(?=...)', '(?!...)'],
'lookbehind' : ['(?<=...)','(?<!...)'],
'caputuring' : ['(?P<name>...)', '(?P=name)', '(?:)'],},
'escapes':{'anchor' : ['^', '\b', '$'],
'non_printable' : ['\n', '\t', '\r', '\f', '\v'],
'shorthand' : ['\d', '\w', '\s']},
'methods': {['search', 'match', 'findall', 'finditer'],
['split', 'sub']},
'match_object': ['group','groups', 'groupdict','start', 'end', 'span',]
}
これは良い質問です。あなたはしばしば人々が理由なしにre.compileを使っているのを見ます。読みやすさが低下します。しかし、式を事前にコンパイルすることが要求されるときが多いことを確認してください。ループなどで繰り返し使用するときのように。
それはプログラミングについてのすべて(実際には人生のすべて)のようなものです。常識を適用する。
私は本当に上記のすべての答えを尊重します。私の意見でははい確かに、正規表現をコンパイルする代わりに、re.compileを何度も使用することをお勧めします。
re.compileを使用すると、再度コンパイルして再度取得するのではなく、すでにコンパイル済みの正規表現を呼び出すことができるため、コードがより動的になります。このことは場合にあなたのためになる:
例:
example_string = "The room number of her room is 26A7B."
find_alpha_numeric_string = re.compile(r"\b\w+\b")
find_alpha_numeric_string.findall(example_string)
find_alpha_numeric_string.search(example_string)
同様にあなたはそれを使うことができます:マッチと代用
Pythonによると ドキュメント :
シーケンス
prog = re.compile(pattern)
result = prog.match(string)
と同等です
result = re.match(pattern, string)
しかし、re.compile()
を使用して、結果として得られる正規表現オブジェクトを再利用のために保存することは、その表現が単一のプログラムで複数回使用される場合にはより効率的です。
だから私の結論は、もしあなたが多くの異なるテキストに対して同じパターンをマッチさせるつもりなら、あなたはそれをより良くプリコンパイルするということです。
(数か月後)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 ...
Wibni、次の場合はいいでしょう:cachehint(size =)、cacheinfo() - > size、hits、nclear ...
私はコンパイル済みの正規表現を何千回も実行するのに対してオンザフライでコンパイルすることに多くの経験を積んできましたが、それほど大きな違いは気づいていません。
受け入れられた答えに対する投票は、@ Triptyが言うことがすべての場合に当てはまるという仮定につながります。これは必ずしも真実ではありません。大きな違いの1つは、関数へのパラメータとして正規表現文字列とコンパイル済み正規表現オブジェクトのどちらを使用するかを決定する必要がある場合です。
>>> timeit.timeit(setup="""
... import re
... f=lambda x, y: x.match(y) # accepts compiled regex as parameter
... h=re.compile('hello')
... """, stmt="f(h, 'hello world')")
0.32881879806518555
>>> timeit.timeit(setup="""
... import re
... f=lambda x, y: re.compile(x).match(y) # compiles when called
... """, stmt="f('hello', 'hello world')")
0.809190034866333
再利用する必要がある場合に備えて、正規表現をコンパイルすることをお勧めします。
上記のtimeitの例は、インポート時に1回コンパイルされた正規表現オブジェクトの作成と、一致に必要な場合の "on-the-fly"の作成をシミュレートしていることに注意してください。
正規表現は、2番目のバージョンを使用するときに使用される前にコンパイルされます。何度も実行する場合は、最初にコンパイルした方が確実に良いでしょう。あなたが1つのオフのためにマッチするたびにコンパイルしないならば大丈夫です。
もう一つの答えとして、私はそれが前に言及されたことがないと思うので、私は先に行き、 Python 3 docs を引用します。
これらのモジュールレベルの関数を使うべきですか、それともパターンを取得して自分でそのメソッドを呼び出すべきですか。ループ内で正規表現にアクセスしている場合は、それを事前にコンパイルしておくと、関数呼び出しが少なくて済みます。ループの外では、内部キャッシュのおかげで大した違いはありません。