파이썬의 re.compile을 사용할 가치가 있습니까?
파이썬에서 정규 표현식으로 컴파일을 사용하면 어떤 이점이 있습니까?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
컴파일 컴파일 정규 표현식을 1000 번 실행하는 것과 비교해 많은 경험을 쌓았으며 인식 할 수있는 차이점을 발견하지 못했습니다. 분명히, 이것은 일화, 그리고 확실히 좋은 인수 에 대한 컴파일,하지만 난 무시할 수의 차이를 발견했습니다.
편집 : 실제 Python 2.5 라이브러리 코드를 간략히 살펴보면 파이썬은 내부적으로 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
나는 종종 정규 표현식을 미리 컴파일하지만 예상되는 성능 향상이 아니라 재사용 가능한 멋진 이름에만 바인딩합니다.
저에게있어 가장 큰 이점 re.compile
은 정규식 정의를 사용과 분리 할 수 있다는 것입니다.
0|[1-9][0-9]*
(제로 0이없는 10 진 정수) 와 같은 간단한 표현조차도 다시 입력하지 않고 오타가 있는지 확인한 다음 나중에 디버깅을 시작할 때 오타가 있는지 다시 확인해야 할 정도로 복잡 할 수 있습니다 . 또한 num 또는 num_b10과 같은 변수 이름을 사용하는 것이 좋습니다 0|[1-9][0-9]*
.
문자열을 저장하고 다시 일치시킬 수 있습니다. 그러나 읽기 쉽지 않습니다 .
num = "..."
# then, much later:
m = re.match(num, input)
컴파일 대 :
num = re.compile("...")
# then, much later:
m = num.match(input)
꽤 가깝지만 두 번째 줄의 마지막 줄은 반복해서 사용할 때 더 자연스럽고 단순합니다.
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(...)
, 즉 컴파일 된 표현의 비하인드 캐싱이 발생하지 않는 것으로 보이는 사례 (마지막 두 실행 사이의 인용 부호 차이에 주목)를 추가했습니다 .
간단한 테스트 사례는 다음과 같습니다.
~$ 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
방법 사용에 따른 추가 비용 은 단순히 캐시 조회 비용입니다.
그러나 코드를 검사 하면 캐시가 100 식으로 제한됩니다. 이것은 캐시를 오버플로하는 것이 얼마나 고통 스럽습니까? 코드에는 정규식 컴파일러에 대한 내부 인터페이스가 포함되어 있습니다 re.sre_compile.compile
. 호출하면 캐시를 무시합니다. 와 같은 기본 정규 표현식의 경우 약 2 배 느리게 나타납니다 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 회만 반복됩니다.
나는 match(...)
주어진 예에서 서로 다른 Honest Abe에 동의합니다 . 그것들은 일대일 비교가 아니므로 결과는 다양합니다. 답장을 단순화하기 위해 해당 기능에 A, B, C, D를 사용합니다. 예, 우리는 re.py
3 대신 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)는 (A)에 의해 호출 된 (D)를 호출 한 후 (B)를 호출합니다. 즉, (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)
우리가 수업을 사용하지 않는다면 (오늘 나의 요청), 나는 의견이 없습니다. 나는 여전히 파이썬에서 전역 변수를 사용하는 법을 배우고 있으며 전역 변수가 나쁜 것임을 알고 있습니다.
한 가지 더 요점 (A) + (B)
은 접근 방식 을 사용 하는 것이 우위에 있다고 생각합니다 . 내가 관찰 한 사실은 다음과 같습니다 (잘못된 경우 수정하십시오).
A를 한 번 호출 하면 정규식 객체를 만들기 위해
_cache
한 번의 검색이 수행됩니다sre_compile.compile()
. A를 두 번 호출하면 두 번의 검색과 한 번의 컴파일이 수행됩니다 (정규 객체가 캐시되기 때문에).경우
_cache
GET 사이에 플러시, 다음 정규식 개체는 메모리와 다시 컴파일 파이썬 필요에서 해제됩니다. (누군가 파이썬이 다시 컴파일하지 않을 것을 제안합니다.)(A)를 사용하여 정규식 객체를 유지하면 정규식 객체는 여전히 _cache로 들어가서 어떻게 든 플러시됩니다. 그러나 우리 코드는 그것에 대한 참조를 유지하며 정규식 객체는 메모리에서 해제되지 않습니다. 그것들은 파이썬이 다시 컴파일 할 필요가 없습니다.
George의 테스트 compileInLoop와 컴파일 된 것의 2 초 차이는 주로 키를 빌드하고 _cache를 검색하는 데 필요한 시간입니다. 정규 표현식의 컴파일 시간을 의미하지는 않습니다.
George의 실제로 컴파일 테스트는 매번 컴파일을 실제로 다시 수행하면 어떻게되는지 보여줍니다. 100 배 느려질 것입니다 (루프를 1,000,000에서 10,000으로 줄였습니다).
(A + B)가 (C)보다 나은 유일한 경우는 다음과 같습니다.
- 클래스 내에서 정규 표현식 객체의 참조를 캐시 할 수 있다면.
- 루프 내부 또는 여러 번 반복적으로 (B)를 호출해야하는 경우 루프 외부의 정규식 객체에 대한 참조를 캐시해야합니다.
(C)가 충분한 경우 :
- 참조를 캐시 할 수 없습니다.
- 우리는 가끔 한 번만 사용합니다.
- 전반적으로, 우리는 너무 많은 정규 표현식을 가지고 있지 않습니다 (컴파일 된 정규 표현식이 결코 플러시되지 않는다고 가정)
요약하면 ABC는 다음과 같습니다.
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()
다른 응답자는 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
위의 분해는 PYC 파일에서 다음을 tmp.py
포함합니다.
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')
일치 상기 예에있어서 이하에 사용 된 것과 동일하지 않다 :
re.match('hello', 'hello world')
re.compile ()는 반환 정규식 개체 수단 h
정규식을 목적으로한다.
정규식 객체에는 선택적 pos 및 endpos 매개 변수 가있는 자체 일치 방법이 있습니다 .
regex.match(string[, pos[, endpos]])
위치
선택적 두 번째 매개 변수 pos 는 검색을 시작할 문자열의 색인을 제공합니다. 기본값은 0입니다. 이는 문자열 슬라이싱과 완전히 같지 않습니다.
'^'
패턴 문자는 있지만 반드시 검색이 시작되는 인덱스, 문자열의 진짜 시작 부분에 단지 개행 후 위치에 일치합니다.
엔포스
선택적 매개 변수 endpos 는 문자열이 검색되는 거리를 제한합니다. 문자열 인 경우로이 될 것입니다 endpos는의 자 길이에서 이렇게 문자 만 POS 에가
endpos - 1
일치하는 검색됩니다. 경우 endpos는이 보다 적은 POS , 일치가 발견되지 않습니다; 그렇지 않으면 rx 가 컴파일 된 정규식 객체 인rx.search(string, 0, 50)
경우와 같습니다rx.search(string[:50], 0)
.
정규식 객체의 search , findall 및 finditer 메소드도 이러한 매개 변수를 지원합니다.
re.match(pattern, string, flags=0)
당신이 볼 수있는 것처럼 그들을 지원하지 않으며,
그것의 search , findall 및 finditer 대응 하지 않습니다 .
일치하는 개체는 이러한 매개 변수를 보완하는 속성이 있습니다 :
match.pos
정규식 객체의 search () 또는 match () 메소드에 전달 된 pos의 값입니다. RE 엔진에서 일치하는 항목을 찾기 시작한 문자열의 색인입니다.
match.endpos
정규식 객체의 search () 또는 match () 메서드에 전달 된 endpos의 값입니다. 이것은 RE 엔진이 가지 않을 문자열에 대한 색인입니다.
정규식 객체는 두 가지 가능성 유용한 고유 한 속성이 있습니다 :
정규식 그룹
패턴에서 캡처 그룹의 수입니다.
정규식 그룹 색인
(? P)로 정의 된 기호 그룹 이름을 그룹 번호에 매핑하는 사전. 패턴에 기호 그룹이 사용되지 않은 경우 사전이 비어 있습니다.
마지막으로 일치 객체 에는 다음 과 같은 속성이 있습니다.
match.re
match () 또는 search () 메서드가이 일치 인스턴스를 생성 한 정규식 객체입니다.
re.compile을 사용하고 컴파일 된 정규 표현식 객체를 사용하여 일치시키는 정규 표현식 관련 작업은 성능 차이를 제외하고 의미론을 Python 런타임에 더 명확하게 만듭니다.
간단한 코드를 디버깅 한 경험이 있습니다.
compare = lambda s, p: re.match(p, s)
나중에 비교를 사용합니다.
[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]
내가 실제로 의미하는 정규 표현식!로 사용됩니다
compare = lambda p, s: p.match(s)
필자의 경우 re.compile을 사용하면 값이 육안으로 숨겨져있을 때 정규 표현식의 목적을보다 명확하게 알 수 있으므로 Python 런타임 검사에서 더 많은 도움을 얻을 수 있습니다.
그래서 내 교훈의 교훈은 정규 표현식이 리터럴 문자열이 아니라면 re.compile을 사용하여 파이썬이 내 가정을 주장하도록 도와야한다는 것입니다.
re.VERBOSE를 사용하여 정규식 패턴에 주석을 추가하는 형태로 re.compile ()을 사용하는 추가 특권이 있습니다.
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
위의 코드를 그대로 한 번 실행하고 두 if
줄로 다른 방법으로 주석을 달면 컴파일 된 정규 표현식이 두 배 빠릅니다.
나는 여기서 토론에 걸림돌이되기 전에이 테스트를 실행했다. 그러나 그것을 실행 한 후에는 적어도 내 결과를 게시 할 것이라고 생각했습니다.
나는 Jeff Friedl의 "Mastering Regular Expressions"에서 예제를 훔쳐 나쁜 놈이었습니다. OSX 10.6 (2Ghz 인텔 코어 2 듀오, 4GB 램)을 실행하는 맥북에 있습니다. 파이썬 버전은 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-재 컴파일을 사용하지 않음
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
이 답변은 늦게 도착할 수 있지만 흥미로운 발견입니다. 정규식을 여러 번 사용할 계획이라면 컴파일을 사용하면 실제로 시간을 절약 할 수 있습니다 (문서에도 언급되어 있음). 아래에서 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
성능 외에.
사용 compile
의 개념을 구별하는 나에게 도움
1. 모듈 (재) ,
2. 정규식 객체
3. 일치하는 개체를
내가 정규식을 배우기 시작했을 때
#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',]
}
파이썬 문서 에 따르면 :
순서
prog = re.compile(pattern)
result = prog.match(string)
에 해당
result = re.match(pattern, string)
그러나 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")
Findall에서 사용
find_alpha_numeric_string.findall(example_string)
검색에서 사용
find_alpha_numeric_string.search(example_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 ...
컴파일 된 정규 표현식을 1000 번 실행하는 것과 비교하여 많은 경험을 쌓았으며 즉각적인 컴파일과는 차이가 없었습니다.
허용 된 답변에 대한 투표는 @Triptych의 말이 모든 경우에 해당된다는 가정으로 이어집니다. 반드시 그런 것은 아닙니다. 한 가지 큰 차이점은 정규식 문자열 또는 컴파일 된 정규식 객체를 함수의 매개 변수로 수락할지 여부를 결정해야하는 경우입니다.
>>> 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에있는 예제는 가져 오기 시간에 한 번 컴파일 된 정규식 객체의 생성과 일치하는 경우 "즉석"생성을 시뮬레이션합니다.
대안적인 답변으로, 이전에 언급되지 않은 것을 보았으므로 파이썬 3 문서를 인용하겠습니다 .
이러한 모듈 레벨 함수를 사용해야합니까, 아니면 패턴을 가져 와서 메소드를 직접 호출해야합니까? 루프 내에서 정규 표현식에 액세스하는 경우 사전 컴파일하면 몇 가지 함수 호출이 저장됩니다. 루프 외부에서는 내부 캐시 덕분에 큰 차이가 없습니다.
정규식은 두 번째 버전을 사용할 때 사용되기 전에 컴파일됩니다. 여러 번 실행하려는 경우 먼저 컴파일하는 것이 좋습니다. 매번 컴파일하지 않으면 하나의 오프에 대해 괜찮습니다.
사전 컴파일이 개념적으로나 '문학적으로'( 'literate programming'에서와 같이) 유리하다는 동기를 부여하고 싶습니다. 이 코드 스 니펫을 살펴보십시오.
from re import compile as _Re
class TYPO:
def text_has_foobar( self, text ):
return self._text_has_foobar_re_search( text ) is not None
_text_has_foobar_re_search = _Re( r"""(?i)foobar""" ).search
TYPO = TYPO()
응용 프로그램에서 다음을 작성하십시오.
from TYPO import TYPO
print( TYPO.text_has_foobar( 'FOObar ) )
기능면에서 가능한 한 간단합니다. 이것은 예제가 너무 짧기 때문에 _text_has_foobar_re_search
한 줄로 모든 것을 얻는 방법을 혼란스럽게했습니다 . 이 코드의 단점은 TYPO
라이브러리 객체 의 수명에 관계없이 약간의 메모리를 차지한다는 것 입니다. 장점은 foobar 검색을 수행 할 때 두 개의 함수 호출과 두 개의 클래스 사전 검색으로 벗어날 수 있다는 것입니다. 얼마나 많은 정규 표현식이 캐시되고 re
그 캐시의 오버 헤드는 여기와 관련이 없습니다.
아래의 일반적인 스타일과 비교하십시오.
import re
class Typo:
def text_has_foobar( self, text ):
return re.compile( r"""(?i)foobar""" ).search( text ) is not None
응용 프로그램에서 :
typo = Typo()
print( typo.text_has_foobar( 'FOObar ) )
나는 내 스타일이 파이썬에서 매우 이례적이며 아마도 논쟁의 여지가 있음을 쉽게 인정한다. 그러나 python이 주로 사용되는 방식과 더 밀접하게 일치하는 예에서 단일 일치를 수행하려면 객체를 인스턴스화하고 3 개의 인스턴스 사전 검색을 수행하고 3 개의 함수 호출을 수행해야합니다. 또한 re
100 개가 넘는 정규 표현식을 사용할 때 캐싱 문제 가 발생할 수 있습니다 . 또한 정규 표현식은 메소드 본문 안에 숨겨져 있으며 대부분의 경우 그렇게 좋은 생각이 아닙니다.
측정의 모든 부분 집합 --- 표적화되고 별명을 가진 수입 명세서; 해당되는 경우 앨리어싱 된 방법; 함수 호출 및 객체 사전 조회 감소 ---- 계산 및 개념적 복잡성을 줄이는 데 도움이 될 수 있습니다.
내 이해는 그 두 가지 예가 사실상 동등하다는 것입니다. 유일한 차이점은 첫 번째에서는 컴파일 된 정규식을 다시 컴파일하지 않고도 다른 곳에서 재사용 할 수 있다는 것입니다.
다음은 참고 자료입니다. http://diveintopython3.ep.io/refactoring.html
문자열 'M'을 사용하여 컴파일 된 패턴 객체의 검색 함수를 호출하면 정규식과 문자열 'M'을 모두 사용하여 re.search를 호출하는 것과 동일한 작업이 수행됩니다. 훨씬 더 빠릅니다. 실제로 re.search 함수는 단순히 정규 표현식을 컴파일하고 결과 패턴 객체의 검색 메소드를 호출합니다.
참고 URL : https://stackoverflow.com/questions/452104/is-it-worth-using-pythons-re-compile
'Programing' 카테고리의 다른 글
디자인 패턴 : 싱글 톤은 언제 사용해야합니까? (0) | 2020.02.20 |
---|---|
배열에서 무언가의 첫 번째 인덱스를 반환하는 NumPy 함수가 있습니까? (0) | 2020.02.20 |
Sublime Text 2에서 정규식 검색 대체 (0) | 2020.02.20 |
출력에서 파이썬 버전 인쇄 (0) | 2020.02.20 |
SQL Server 2008을 사용하여 IDENTITY_INSERT를 설정 및 해제하는 방법은 무엇입니까? (0) | 2020.02.20 |