파이썬의 re.compile을 사용할 가치가 있습니까?


461

파이썬에서 정규 표현식으로 컴파일을 사용하면 어떤 이점이 있습니까?

h = re.compile('hello')
h.match('hello world')

vs

re.match('hello', 'hello world')

8
2.6 re.sub에서 플래그 인수를받지 않을 것이라는 사실도 있습니다.
new123456

58
방금 사용 re.compile하면 10-50 배 개선 된 사례가 발생했습니다. 도덕적이다 경우에 당신이 (MAXCACHE = 100 개 이상) 정규 표현식에 많이있다 그리고 당신이 MAXCACHE 이상으로 그들에게 회 (분리를 많이 사용하는 각각의 하나는 캐시에서 플러시 가져옵니다 사이에서 정규 표현식에 그래서 사용 같은 시간을 여러 번 누른 다음 다음으로 넘어가는 것은 중요하지 않습니다.) 그러면 컴파일하는 데 도움이됩니다. 그렇지 않으면 차이가 없습니다.
ShreevatsaR

8
한 가지 작은 점은 정규 표현식이 필요없는 in문자열의 경우 문자열 하위 문자열 테스트가 훨씬 빠릅니다.>python -m timeit -s "import re" "re.match('hello', 'hello world')" 1000000 loops, best of 3: 1.41 usec per loop >python -m timeit "x = 'hello' in 'hello world'" 10000000 loops, best of 3: 0.0513 usec per loop
Gamrix

@ShreevatsaR 재미있는! 10x-50x 향상을 보여주는 예제로 답변을 게시 할 수 있습니까? 여기에 주어진 대부분의 답변은 실제로 정확한 경우에는 3 배 개선되었으며 다른 경우에는 거의 개선되지 않았습니다.
Basj

1
@Basj Done 님 이 답변을 게시 했습니다 . 2013 년 12 월에 내가 파이썬을 사용하고있는 것을 파헤 치려고하지 않았지만, 내가 시도한 첫 번째 것은 같은 행동을 보여줍니다.
ShreevatsaR

답변:


436

컴파일 된 정규 표현식을 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

나는 종종 정규 표현식을 미리 컴파일하지만 예상되는 성능 향상이 아니라 재사용 가능한 멋진 이름에만 바인딩합니다.


12
당신의 결론은 당신의 대답과 일치하지 않습니다. 정규식이 자동으로 컴파일되어 저장되면 대부분의 경우 직접 수작업을 수행 할 필요가 없습니다.
jfs

84
JF Sebastian은 문제의 정규 표현식이 많이 사용될 것이라는 프로그래머에게 신호 역할을하며 버리기위한 것이 아닙니다.
kaleissin

40
또한 응용 프로그램의 성능에 중요한 부분에서 컴파일 및 캐시 적중을 겪고 싶지 않다면 응용 프로그램의 중요하지 않은 부분에서 미리 컴파일하는 것이 가장 좋습니다. .
Eddie Parker

20
동일한 정규 표현식을 여러 번 다시 사용하면 컴파일 된 정규 표현식을 사용하면 오타의 가능성이 줄어 듭니다. 방금 한 번 호출하면 컴파일되지 않은 것이 더 읽기 쉽습니다.
monkut

18
따라서 주요 차이점은 다른 정규 표현식 (_MAXCACHE 이상)을 많이 사용하고 일부는 한 번만 사용하고 다른 경우는 여러 번 사용하는 경우입니다. 그러면 더 많이 사용되는 표현식을 유지하는 것이 중요합니다. 캐시가 가득 차면 캐시에서 플러시되지 않습니다.
포트란

133

저에게있어 가장 큰 이점 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)

꽤 가깝지만 두 번째 줄의 마지막 줄은 반복해서 사용할 때 더 자연스럽고 단순합니다.


5
이 답변에 동의합니다. 종종 re.compile을 사용하면 읽을 수있는 코드가 더 많지 않습니다.
Carl Meyer

1
예를 들어 정규식을 한 곳에서 정의하고 다른 멀리 떨어진 곳에서 일치하는 그룹을 사용하는 경우에는 반대의 경우가 있습니다.
Ken Williams

1
@KenWilliams 반드시 특정 목적을 위해 잘 정의 된 정규식은 원래 정의에서 멀리 사용하더라도 명확해야합니다. 예를 들어, us_phone_number또는 social_security_number
브라이언 M. 셀던

2
@ BrianM.Sheldon은 정규 표현식의 이름을 잘 지정하여 다양한 캡처 그룹이 무엇을 나타내는 지 실제로 알지 못합니다.
Ken Williams

68

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(...), 즉 컴파일 된 표현의 비하인드 캐싱이 발생하지 않는 것으로 보이는 사례 (마지막 두 실행 사이의 인용 부호 차이에 주목)를 추가했습니다 .


5
설정 인수가 타이밍에 포함되지 않으므로 여기에서 방법론의 주요 문제점. 따라서 두 번째 예제에서 컴파일 시간을 제거하고 첫 번째 예제에서 평균을 계산했습니다. 그렇다고해서 첫 번째 예제가 매번 컴파일되는 것은 아닙니다.
삼부작

1
네, 이것이 두 경우의 공정한 비교가 아니라는 것에 동의합니다.
Kiv

7
나는 당신이 무슨 뜻인지 알지만, 정규 표현식이 여러 번 사용되는 실제 응용 프로그램에서 정확히 어떻게됩니까?
dF.

26
@Triptych, @Kiv는 : regexps '에 사용에서 분리 컴파일의 요점 이다 컴파일을 최소화하기 위해; 타이밍에서 제거하는 것은 dF가해야 할 일입니다. 실제 사용이 가장 정확하기 때문입니다. 컴파일 시간은 timeit.py가 타이밍을 수행하는 방식과 특히 관련이 없습니다. 여러 실행을 수행하고 가장 짧은 실행 만보고하며이 시점에서 컴파일 된 regexp가 캐시됩니다. 여기에 표시되는 추가 비용은 정규 표현식을 컴파일하는 비용이 아니라 컴파일 된 정규 표현식 캐시 (사전)에서 찾는 비용입니다.
jemfinch

3
@Triptych import re설정 에서 벗어나야합니까 ? 측정하려는 곳이 전부입니다. 파이썬 스크립트를 여러 번 실행하면 import re시간 이 걸릴 것 입니다. 둘을 비교할 때 타이밍을 위해 두 라인을 분리하는 것이 중요합니다. 네가 말한대로 당신은 시간을 맞출 것입니다. 비교 결과는 적중 한 시간이 걸리고 컴파일을 통해 적중 한 시간이 짧아 지거나 호출 사이에 캐시가 지워 졌다고 가정 할 때마다 적중을 가하는 것으로 나타났습니다. 타이밍을 추가하면 h=re.compile('hello')명확히하는 데 도움이됩니다.
Tom Myddeltyn

39

간단한 테스트 사례는 다음과 같습니다.

~$ 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

따라서 한 번만 일치 하더라도이 간단한 경우 컴파일이 더 빠릅니다 .


2
어떤 버전의 Python입니까?
Kyle Strand

2
중요한 것은 요점은 코드를 실행할 환경에서 벤치 마크를 시도하는 것입니다.
david king

1
나에게 1000 루프 이상의 성능은 거의 동일합니다. 컴파일 된 버전은 1-100 루프에 더 빠릅니다. (파이썬 2.7과 3.4에서).
Zitrax

2
내 Python 2.7.3 설정에는 거의 차이가 없습니다. 때로는 컴파일 속도가 빠르며 때로는 느리지 않습니다. 차이는 항상 <5 %이므로 장치에는 하나의 CPU 만 있기 때문에 불확실성을 측정하는 것으로 차이를 계산합니다.
Dakkaron

1
Python 3.4.3에서는 두 개의 개별 실행에서 볼 수 있습니다 .compiled를 사용하는 것이 컴파일되지 않은 것보다 느립니다.
Zelphir Kaltstahl

17

방금 직접 시도했습니다. 문자열에서 숫자를 파싱하고 합산하는 간단한 경우 컴파일 된 정규식 객체를 사용하는 것이 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 회만 반복됩니다.


컴파일 된 정규 표현식이 컴파일되지 않은 것보다 훨씬 빠르게 실행된다는 데 동의합니다. 정규 표현식이 컴파일되지 않았을 때 10,000 개가 넘는 문장을 실행하고 정규 표현식을 반복하기 위해 루프를 만들었습니다. 정규 실행 패턴이 컴파일 된 색인에 따라 사전을 작성한 후 전체 실행의 예측이 8 시간마다 계산되었습니다. 2 분 동안 모든 것. 위의 답변을 이해할 수 없습니다 ...
Eli Borodach

12

나는 match(...)주어진 예에서 서로 다른 Honest Abe에 동의합니다 . 그것들은 일대일 비교가 아니므로 결과는 다양합니다. 답장을 단순화하기 위해 해당 기능에 A, B, C, D를 사용합니다. 예, 우리는 re.py3 대신 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)를 호출합니다. 즉, (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)은 접근 방식 을 사용 하는 것이 우위에 있다고 생각합니다 . 내가 관찰 한 사실은 다음과 같습니다 (잘못되면 수정하십시오).

  1. A를 한 번 호출 하면 정규식 객체를 만들기 위해 _cache한 번의 검색이 수행됩니다 sre_compile.compile(). A를 두 번 호출하면 정규식 객체가 캐시되기 때문에 두 번의 검색과 한 번의 컴파일이 수행됩니다.

  2. 경우 _cacheGET 사이에 플러시, 다음 정규식 개체는 메모리와 다시 컴파일 파이썬 필요에서 해제됩니다. (누군가 파이썬이 다시 컴파일하지 않을 것을 제안합니다.)

  3. (A)를 사용하여 정규식 객체를 유지하면 정규식 객체는 여전히 _cache로 들어가서 어떻게 든 지워집니다. 그러나 우리 코드는 그것에 대한 참조를 유지하며 정규식 객체는 메모리에서 해제되지 않습니다. 그것들은 파이썬이 다시 컴파일 할 필요가 없습니다.

  4. George의 테스트 compileInLoop와 컴파일 된 것의 2 초 차이는 주로 키를 빌드하고 _cache를 검색하는 데 필요한 시간입니다. 정규 표현식의 컴파일 시간을 의미하지는 않습니다.

  5. George의 실제로 컴파일 테스트는 매번 컴파일을 실제로 다시 수행하면 어떻게되는지 보여줍니다. 100 배 느려질 것입니다 (루프를 1,000,000에서 10,000으로 줄였습니다).

(A + B)가 (C)보다 나은 유일한 경우는 다음과 같습니다.

  1. 클래스 내에서 정규 표현식 객체의 참조를 캐시 할 수 있다면.
  2. 루프 내부 또는 여러 번 반복적으로 (B)를 호출해야하는 경우 루프 외부의 정규식 객체에 대한 참조를 캐시해야합니다.

(C)가 충분한 경우 :

  1. 참조를 캐시 할 수 없습니다.
  2. 우리는 가끔 한 번만 사용합니다.
  3. 전반적으로, 우리는 너무 많은 정규 표현식을 가지고 있지 않습니다 (컴파일 된 정규 표현식이 결코 플러시되지 않는다고 가정)

요약하면 ABC는 다음과 같습니다.

h = re.compile('hello')                   # (A)
h.match('hello world')                    # (B)
re.match('hello', 'hello world')          # (C)

읽어 주셔서 감사합니다.


8

대부분 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}')

1
"def search(pattern, string, flags=0):"오타?
phuclv

1
경우주의 pattern이미 컴파일 된 패턴, 캐싱 오버 헤드 중요하게하십시오 해싱은 SRE_Pattern비싸고 조회는 매번 실패 때문에 패턴은 캐시에 기록되지 않습니다 KeyError.
Eric Duminil

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']

re.findall어쨌든 플래그를 세 번째 인수로 사용할 수 있습니다 .
aderchox

5

주어진 예제를 사용하여 :

h = re.compile('hello')
h.match('hello world')

일치 상기 예에있어서 이하에 사용 된 것과 동일하지 않다 :

re.match('hello', 'hello world')

re.compile ()는 반환 정규식 개체 수단 h정규식을 목적으로한다.

정규식 객체에는 선택적 posendpos 매개 변수 가있는 자체 일치 방법이 있습니다 .

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).

정규식 객체의 검색 , findallfinditer 메소드도 이러한 매개 변수를 지원합니다.

re.match(pattern, string, flags=0)당신이 볼 수 있듯이을 지원하지 않습니다,
이나 그 않는 검색을 , findallfinditer 대응 .

일치하는 개체는 이러한 매개 변수를 보완하는 속성이 있습니다 :

match.pos

정규식 객체의 search () 또는 match () 메소드에 전달 된 pos의 값입니다. 이것은 RE 엔진이 일치를 찾기 시작한 문자열의 색인입니다.

match.endpos

정규식 객체의 search () 또는 match () 메서드에 전달 된 endpos의 값입니다. 이것은 RE 엔진이 가지 않을 문자열에 대한 색인입니다.


정규식 객체는 두 가지 가능성 유용한 고유 한 속성이 있습니다 :

정규식 그룹

패턴에서 캡처 그룹의 수입니다.

정규식 그룹 색인

(? P)로 정의 된 기호 그룹 이름을 그룹 번호에 매핑하는 사전. 패턴에 기호 그룹이 사용되지 않은 경우 사전이 비어 있습니다.


마지막으로 일치하는 개체 에는 다음 같은 속성이 있습니다.

match.re

match () 또는 search () 메서드가이 일치 인스턴스를 생성 한 정규식 객체입니다.


4

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을 사용하여 파이썬이 내 가정을 주장하도록 도와야한다는 것입니다.


4

re.VERBOSE를 사용하여 정규식 패턴에 주석을 추가하는 형태로 re.compile ()을 사용하는 추가 특권이 있습니다.

pattern = '''
hello[ ]world    # Some info on my pattern logic. [ ] to recognize space
'''

re.search(pattern, 'hello world', re.VERBOSE)

이것은 코드 실행 속도에 영향을 미치지 않지만 주석 처리 습관의 일부 이므로이 방법을 선호합니다. 나는 수정을 원할 때 2 개월 내 코드 뒤에 남은 논리를 기억하려고 노력하는 데 시간을 보내는 것을 좋아하지 않습니다.


1
답변을 수정했습니다. 나는 언급하는 re.VERBOSE것이 가치가 있다고 생각하며 다른 답변이 생략 된 것으로 보입니다. 그러나 "아직 댓글을 달 수 없기 때문에 게시하고 있습니다"라는 답이 나오면 반드시 삭제해야합니다. 답변 이외의 다른 답변 상자를 사용하지 마십시오. 당신은 어디에서나 (50 담당자) 의견을 말할 수있는 한두 가지 좋은 답변이므로, 인내심을 가지십시오. 답을 보내면 안된다고 답할 때 답을 상자에 넣는 것이 더 빠릅니다. 다운 보트와 삭제 된 답변을 얻을 수 있습니다.
skrrgwasme

4

파이썬 문서 에 따르면 :

순서

prog = re.compile(pattern)
result = prog.match(string)

에 해당

result = re.match(pattern, string)

그러나 re.compile()단일 프로그램에서 표현식을 여러 번 사용할 경우 결과 정규식 오브젝트를 재사용 하여 저장하는 것이 더 효율적입니다.

그래서 제 결론은, 당신이 많은 다른 텍스트들에 대해 같은 패턴을 일치 시키려면 미리 컴파일하는 것이 좋습니다.


3

흥미롭게도, 컴파일이 나에게 더 효과적이라는 것이 증명되었습니다 (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줄로 다른 방법으로 주석을 달면 컴파일 된 정규 표현식이 두 배 빠릅니다.


2
dF의 성능 비교와 동일한 문제입니다. 컴파일 문 자체의 성능 비용을 포함시키지 않으면 실제로 불공평합니다.
Carl Meyer

6
칼, 동의하지 않아 일치하는 루프를 백만 번 실행되는 동안 컴파일은 한 번만 실행
엘리 Bendersky을

@eliben : Carl Meyer에 동의합니다. 컴파일은 두 경우 모두에서 발생합니다. Triptych는 캐싱이 관련되어 있다고 언급하므로 최적의 경우 (캐시에 머물러 있음) 두 접근 방식은 모두 O (n + 1)이지만 re.compile을 명시 적으로 사용하지 않으면 +1 부분이 숨겨져 있습니다.
paprika

1
자체 벤치마킹 코드를 작성하지 마십시오. 표준 배포판에 포함 된 timeit.py 사용법을 배웁니다.
jemfinch

그 시간 동안 for 루프에서 패턴 문자열을 다시 작성하는 중입니다. 이 오버 헤드는 사소 할 수 없습니다.
IceArdor

3

나는 여기서 토론에 걸림돌이되기 전에이 테스트를 실행했다. 그러나 그것을 실행하면 적어도 내 결과를 게시 할 것이라고 생각했습니다.

나는 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

3

이 답변은 늦게 도착할 수 있지만 흥미로운 발견입니다. 정규식을 여러 번 사용할 계획이라면 컴파일을 사용하면 실제로 시간을 절약 할 수 있습니다 (문서에도 언급되어 있음). 아래에서 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

3

성능 외에.

사용 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',]
}

2

나는 위의 모든 대답을 정말로 존중합니다. 내 의견으로는 그렇습니다! 매번 정규 표현식을 반복해서 컴파일하는 대신 re.compile을 사용하는 것이 좋습니다.

re.compile 을 사용하면 다시 컴파일하고 다시 컴파일하는 대신 이미 컴파일 된 정규식을 호출 할 수 있으므로 코드를보다 동적으로 만들 수 있습니다. 이 경우에는 다음과 같은 이점이 있습니다.

  1. 프로세서 노력
  2. 시간 복잡성.
  3. 정규식을 범용으로 만듭니다. (찾기, 검색, 일치에 사용할 수 있음)
  4. 그리고 프로그램이 멋지게 보입니다.

예 :

  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)

마찬가지로 다음 과 같은 용도로 사용할 수 있습니다.


1

좋은 질문입니다. 사람들이 이유없이 re.compile을 사용하는 것을 종종 볼 수 있습니다. 가독성이 떨어집니다. 그러나 표현식을 사전 컴파일 할 때 많은 시간이 필요합니다. 루프 등에서 반복해서 사용하는 것과 같습니다.

그것은 프로그래밍에 관한 모든 것과 같습니다 (실제로 모든 것). 상식을 적용하십시오.


간단히 말해서 알 수 있듯이 Nutshell의 Python은 re.compile ()없이 사용하는 것에 대해 언급하지 않아 호기심이 생겼습니다.
Mat

정규식 객체는 컨텍스트에 객체를 하나 더 추가합니다. 내가 말했듯이 re.compile ()이 많은 상황이 있습니다. OP가 제시 한 예는 그중 하나가 아닙니다.
PEZ

1

(수개월 후) 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 ...


1

컴파일 된 정규 표현식을 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에있는 예제는 가져 오기 시간에 한 번 컴파일 된 정규식 객체의 생성과 일치하는 경우 "즉석"생성을 시뮬레이션합니다.


1

대안적인 대답으로, 이전에 언급되지 않은 것을 보았으므로 파이썬 3 문서를 인용하겠습니다 .

이러한 모듈 레벨 함수를 사용해야합니까, 아니면 패턴을 가져 와서 메소드를 직접 호출해야합니까? 루프 내에서 정규 표현식에 액세스하는 경우 사전 컴파일하면 몇 가지 함수 호출이 저장됩니다. 루프 외부에서는 내부 캐시 덕분에 큰 차이가 없습니다.


1

다음은 요청re.compile 에 따라 사용 이 50 배 이상 빠른 예 입니다.

요점은 위의 주석에서 작성한 것과 동일합니다. 즉, 사용 re.compile이 컴파일 캐시의 이점을 많이 누리지 못하는 경우를 사용하면 상당한 이점이 될 수 있습니다. 이것은 적어도 하나의 특정 경우 (실제로 실행 한 경우), 즉 다음 사항이 모두 해당되는 경우에 발생합니다.

  • 당신은 (정규식 패턴을 많이 가지고 더 이상 re._MAXCACHE누구의 기본 현재 512), 및
  • 이 정규 표현식을 여러 번 사용하고
  • 동일한 패턴의 연속 사용은 그 re._MAXCACHE사이에 다른 정규 표현식 보다 더 많이 구분 되므로 각 사용은 연속 사용간에 캐시에서 플러시됩니다.
import re
import time

def setup(N=1000):
    # Patterns 'a.*a', 'a.*b', ..., 'z.*z'
    patterns = [chr(i) + '.*' + chr(j)
                    for i in range(ord('a'), ord('z') + 1)
                    for j in range(ord('a'), ord('z') + 1)]
    # If this assertion below fails, just add more (distinct) patterns.
    # assert(re._MAXCACHE < len(patterns))
    # N strings. Increase N for larger effect.
    strings = ['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'] * N
    return (patterns, strings)

def without_compile():
    print('Without re.compile:')
    patterns, strings = setup()
    print('searching')
    count = 0
    for s in strings:
        for pat in patterns:
            count += bool(re.search(pat, s))
    return count

def without_compile_cache_friendly():
    print('Without re.compile, cache-friendly order:')
    patterns, strings = setup()
    print('searching')
    count = 0
    for pat in patterns:
        for s in strings:
            count += bool(re.search(pat, s))
    return count

def with_compile():
    print('With re.compile:')
    patterns, strings = setup()
    print('compiling')
    compiled = [re.compile(pattern) for pattern in patterns]
    print('searching')
    count = 0
    for s in strings:
        for regex in compiled:
            count += bool(regex.search(s))
    return count

start = time.time()
print(with_compile())
d1 = time.time() - start
print(f'-- That took {d1:.2f} seconds.\n')

start = time.time()
print(without_compile_cache_friendly())
d2 = time.time() - start
print(f'-- That took {d2:.2f} seconds.\n')

start = time.time()
print(without_compile())
d3 = time.time() - start
print(f'-- That took {d3:.2f} seconds.\n')

print(f'Ratio: {d3/d1:.2f}')

랩탑 (Python 3.7.7)에 출력되는 예제 출력 :

With re.compile:
compiling
searching
676000
-- That took 0.33 seconds.

Without re.compile, cache-friendly order:
searching
676000
-- That took 0.67 seconds.

Without re.compile:
searching
676000
-- That took 23.54 seconds.

Ratio: 70.89

timeit차이가 너무나 빠르기 때문에 신경 쓰지 않았지만 매번 질적으로 비슷한 숫자를 얻습니다. 없이도 re.compile동일한 정규 표현식을 여러 번 사용하고 다음 정규 표현식으로 이동하는 것은 그리 나쁘지 않지만 (와 함께 약 2 배 느리지 만 re.compile) 다른 순서로 (많은 정규 표현식을 반복하여), 그것은 훨씬 더 나쁩니다. 예상대로. 또한, 캐시 크기를 증가시키는 것은 너무 작동 : 단순히 설정 re._MAXCACHE = len(patterns)setup()~ 23초가 ~ 0.7 초 아래로 다시 떨어질 (밑줄 이름은 통상적으로 "개인"대로 물론 생산에서 그런 일을하지 않는 것이 좋습니다의) 이상도있는 우리의 이해와 일치합니다.


추신 : 전체 코드에서 3 개의 정규식 패턴 사용 하고 각 패턴이 특정 순서없이 수백 번 사용 된 경우 정규식 캐시는 사전 컴파일 된 정규식을 자동으로 유지합니다.
Basj

@Basj 나는 당신이 그것을 시도하고 볼 수 있다고 생각합니다 :) 그러나 대답은 확실히 그렇습니다. 그 경우 AFAICT의 유일한 추가 비용은 단순히 캐시에서 패턴을 찾는 것의 비용입니다 . 캐시는 전역 (모듈 수준)이므로 원칙적으로 정규식 검색을 수행하는 종속성 라이브러리가있을 수 있으므로 프로그램에서 정규식 (또는 여러 수) 만 사용한다는 것을 확신하기가 어렵습니다. 패턴, 그러나 그렇지 않으면 꽤 이상 할 것입니다 :)
ShreevatsaR

0

정규식은 두 번째 버전을 사용할 때 사용되기 전에 컴파일됩니다. 여러 번 실행하려는 경우 먼저 컴파일하는 것이 좋습니다. 한 번의 오프를 위해 매번 컴파일 할 수 없다면 괜찮습니다.


0

가독성 /인지 부하 환경 설정

나를 위해, 주요 이득 난 단지, 기억하고 읽을 필요가있다 하나 개 더 - 복잡한 정규식 API 구문의 형태 <compiled_pattern>.method(xxx)가 아니라보다 형태 re.func(<pattern>, xxx) 양식을.

그만큼 re.compile(<pattern>) 약간의 추가 상용구입니다.

그러나 정규 표현식과 관련하여 여분의 컴파일 단계가인지 부하의 큰 원인이 될 가능성은 없습니다. 사실 복잡한 패턴에서는 선언식을 호출 한 정규식 메소드에서 선언을 분리하여 명확성을 얻을 수도 있습니다.

먼저 Regex101과 같은 웹 사이트 또는 별도의 최소 테스트 스크립트에서 복잡한 패턴을 조정 한 다음 내 코드로 가져 와서 사용과 선언을 분리하면 워크 플로우에도 적합합니다.


-1

사전 컴파일이 개념적으로나 '문학적으로'( '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 개가 넘는 정규 표현식을 사용할 때 캐싱 문제 . 또한 정규 표현식은 메소드 본문 안에 숨겨져 있으며 대부분의 경우 그렇게 좋은 생각이 아닙니다.

측정의 모든 부분 집합 --- 표적화되고 별명 된 수입 명세서; 해당되는 경우 앨리어싱 된 방법; 함수 호출 및 객체 사전 조회 감소 ---- 계산 및 개념적 복잡성을 줄이는 데 도움이 될 수 있습니다.


2
WTF. 당신은 오래되고 답이되는 질문을 쏟을뿐만 아니라 여러분의 코드는 너무 많은 수준에서 비 이용적이고 잘못 되었습니다. (ab) 클래스가 충분한 네임 스페이스로 클래스를 사용하고, 클래스 이름을 대문자로하는 등 ... 더 나은 구현에 대해서는 pastebin.com/iTAXAWen 을 참조하십시오 . 당신이 사용하는 정규식은 말할 것도 없습니다. 전체 -1

2
저지른. 이것은 오래된 질문이지만 느려진 대화에서 # 100 인 것은 괜찮습니다. 질문이 종결되지 않았습니다. 나는 내 코드가 어떤 취향에 해를 끼칠 수 있다고 경고했다. 파이썬에서 할 수있는 일에 대한 단순한 시연으로 볼 수 있다면, 우리가 모든 것을 취한다면, 우리가 믿는 모든 것을 선택 사항으로 생각한 다음 어떤 방식 으로든 함께 땜질하면 우리가 할 수있는 것처럼 보이는 것은 무엇입니까? 가져 오기? 나는 당신이이 해결책의 장점과 단점을 분별할 수 있고 더 분명하게 불평 할 수 있다고 확신합니다. 그렇지 않으면 나는 당신의 잘못에 대한 주장이 PEP008 이상에 의존한다고 결론을 내릴 수 있습니다
흐름

2
아니요, PEP8에 관한 것이 아닙니다. 그것은 단지 명명 규칙이며, 나는 그것을 따르지 않기로 결코 투표하지 않았습니다. 당신이 보여준 코드가 제대로 작성되지 않았기 때문에 당신에게 하향 투표를했습니다. 그것은 아무런 이유없이 관습과 관용구를 무시하고 영구적 인 최적화의 화신입니다 : 당신은 병목 현상을 일으키기 위해 다른 모든 코드 중에서 살아있는 일광을 최적화해야하며, 심지어 내가 제공 한 세 번째 재 작성은 더 짧습니다. 당신의 추론 (동일한 수의 속성 액세스)으로 관용 적이고 빠릅니다.

왜 "정확하게 쓰여지지 않았다"? "컨벤션과 관용구를 정의"-경고합니다. "아무 이유없이"-그렇습니다. 이유가 있습니다 : 복잡성이 목적을 제공하지 않는 곳을 단순화하십시오; "조기 최적화의 화신"-가독성과 효율성의 균형을 선택하는 프로그래밍 스타일에 매우 중요합니다. OP는 효율성에 대한 질문으로 이해하는 "re.compile 사용의 이점"에 대한 언급을 요구했습니다. "(ab) 클래스를 네임 스페이스로 사용하는 것"-당신의 말은 욕설입니다. 수업 자체가 있으므로 "자체"관점이 있습니다. 이 목적으로 모듈을 사용하려고 시도하면 클래스가 더 잘 작동합니다.
흐름

"자본 클래스 이름", "아니오, PEP8에 관한 것이 아닙니다."-당신은 무엇을 먼저 울어야할지조차 말할 수 없을 정도로 너무 화가 난 것 같습니다. "WTF", " 틀린 "--- 정말 기분이 어떻습니까? 더 객관적이고 적은 거품을주세요.
흐름

-5

내 이해는 그 두 가지 예가 사실상 동등하다는 것입니다. 유일한 차이점은 첫 번째에서는 컴파일 된 정규식을 다시 컴파일하지 않고도 다른 곳에서 재사용 할 수 있다는 것입니다.

다음은 참고 자료입니다. http://diveintopython3.ep.io/refactoring.html

문자열 'M'을 사용하여 컴파일 된 패턴 객체의 검색 함수를 호출하면 정규식과 문자열 'M'을 모두 사용하여 re.search를 호출하는 것과 동일한 작업이 수행됩니다. 훨씬 더 빠릅니다. 실제로 re.search 함수는 단순히 정규 표현식을 컴파일하고 결과 패턴 객체의 검색 메소드를 호출합니다.


1
난 당신을 downvote하지 않았지만 기술적으로 이것은 잘못입니다 : 파이썬은 어쨌든 다시 컴파일하지 않습니다
Triptych
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.