575 votes

Cela vaut-il la peine d'utiliser le fichier re.compile de Python?

Y a-t-il un avantage à utiliser compile pour les expressions régulières en Python?

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

contre

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

512voto

Triptych Points 70247

J'ai eu beaucoup d'expérience en cours d'exécution, constitué à partir des regex 1000s de fois contre la compilation à la volée, et n'ai pas remarqué de différence perceptible. Évidemment, c'est familier, et certainement pas un grand argument à l'encontre de la compilation, mais j'ai trouvé la différence sera négligeable.

EDIT: Après un rapide coup d'œil à l'Python 2.5 bibliothèque de code, je vois que Python en interne, les compile ET les CACHES regexes chaque fois que vous les utiliser de toute façon (y compris les appels à l' re.match()), de sorte que vous êtes vraiment en changeant uniquement LORSQUE la regex sera compilé, et ne devrait pas être de sauver beaucoup de temps, seulement le temps qu'il faut pour vérifier le cache (une clé de recherche à l'interne en dict type).

De module re.py (les commentaires sont de la mienne):

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

J'ai encore souvent des pré-compilation des expressions régulières, mais seulement pour les lier à une belle, réutilisable nom, pas pour tout gain de performance.

95voto

dF. Points 29787

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
 

Donc, si vous utilisez beaucoup la même regex, ça vaut la peine de faire re.compile (surtout pour les regex plus complexes).

Les arguments standard contre l'optimisation prématurée s'appliquent, mais je ne pense pas que vous perdiez vraiment beaucoup de clarté / simplicité en utilisant re.compile si vous pensez que vos expressions régulières peuvent devenir un goulot d'étranglement.

44voto

david king Points 316

Voici un cas de test simple:

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

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

Donc, il semblerait que la compilation est plus rapide avec ce cas simple, même si vous ne correspondez qu'une seule fois .

21voto

George Points 945

J'ai juste essayé moi-même. Pour le cas simple de l'analyse d'un numéro à l'intérieur d'une chaîne et d'en résumé, à l'aide d'une expression régulière compilée objet est environ deux fois plus rapide que l'utilisation de l' re méthodes.

Comme d'autres l'ont souligné, l' re méthodes (y compris re.compile) pour l'expression régulière dans une cache d'précédemment compilé expressions. Par conséquent, dans le cas normal, le coût supplémentaire de l'utilisation de l' re méthodes est simplement le coût de la recherche dans le cache.

Toutefois, l'examen du code, montre que le cache est limité à 100 expressions. Cela soulève la question, à quel point est-il de débordement de la mémoire cache? Le code contient une interface interne à l'expression régulière compilateur, re.sre_compile.compile. Si nous l'appelons, nous contourner le cache. Il s'avère à être d'environ deux ordres de grandeur plus lent pour une base de l'expression régulière, comme r'\w+\s+([0-9_]+)\s+\w*'.

Voici mon test:

#!/usr/bin/env python
importation re
le délai d'importation

def chronométré(func):
 def wrapper(*args):
 t = temps.temps (le)
 résultat = func(*args)
 t = temps.time() - t
 print '%s a duré %.3f secondes.' % (func.func_name, t)
 résultat de retour
 retour wrapper

regularExpression = r'\w+\s+([0-9_]+)\s+\w*'
chaîne de test = "moyenne 2 jamais"

@chronométré
def noncompiled():
 a = 0
 pour x in xrange(1000000):
 m = re.match(regularExpression, chaîne de test)
 a += int(m.groupe(1))
 de retour d'un

@chronométré
def compilé():
 a = 0
 rgx = re.compiler(regularExpression)
 pour x in xrange(1000000):
 m = rgx.match(de test)
 a += int(m.groupe(1))
 de retour d'un

@chronométré
def reallyCompiled():
 a = 0
 rgx = re.sre_compile.compiler(regularExpression)
 pour x in xrange(1000000):
 m = rgx.match(de test)
 a += int(m.groupe(1))
 de retour d'un


@chronométré
def compiledInLoop():
 a = 0
 pour x in xrange(1000000):
 rgx = re.compiler(regularExpression)
 m = rgx.match(de test)
 a += int(m.groupe(1))
 de retour d'un

@chronométré
def reallyCompiledInLoop():
 a = 0
 pour x in xrange(10000):
 rgx = re.sre_compile.compiler(regularExpression)
 m = rgx.match(de test)
 a += int(m.groupe(1))
 de retour d'un

r1 = noncompiled()
r2 = compilé()
r3 = reallyCompiled()
r4 = compiledInLoop()
r5 = reallyCompiledInLoop()
print "r1 =" r1
print "r2 = ", r2
print "r3 = ", r3
print "r4 = ", r4
print "r5 = ", r5

Et voici le résultat sur ma machine:

$ regexTest.py 
noncompiled a pris 4.555 secondes.
compilé pris 2.323 secondes.
reallyCompiled a pris 2.325 secondes.
compiledInLoop a pris 4.620 secondes.
reallyCompiledInLoop a pris 4.074 secondes.
r1 = 2000000
r2 = 2000000
r3 = 2000000
r4 = 2000000
r5 = 20000

Le " reallyCompiled des méthodes d'utilisation de l'interface interne, qui contourne le cache. Note celui qui compile sur chaque itération de boucle n'est répété 10 000 fois, pas un million.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X