198 votos

¿Vale la pena usar Python ' s re.compile?

¿Existe algún beneficio en el uso de compilación para las expresiones regulares en Python.

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

vs

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

217voto

Triptych Puntos 70247

He tenido un montón de experiencia en la conducción de un compilado de regex de 1000 veces frente a la compilación sobre la marcha, y no han notado ninguna diferencia perceptible. Obviamente, este es coloquial, y ciertamente no es un gran argumento en contra de la compilación, pero he encontrado la diferencia para ser insignificante.

EDICIÓN: Después de un rápido vistazo en el real Python 2.5 del código de la biblioteca, veo que Python internamente recopila Y almacena expresiones regulares cuando se usan de todas maneras (incluyendo llamadas a re.match()), por lo que es realmente y sólo cambia CUANDO el regex se compila, y no debería estar ahorrando mucho tiempo y que sólo el tiempo necesario para comprobar la memoria caché (clave de búsqueda interno dict tipo).

Desde el módulo de re.py (los comentarios son mías):

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

Sigo a menudo pre-compilación de expresiones regulares, pero sólo a ellos se unen a un agradable, reutilizables nombre, no por el rendimiento esperado de la ganancia.

29voto

dF. Puntos 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

Entonces, si vas a usar el mismo regex mucho, puede ser vale la pena hacer re.compile (especialmente para regexes más complejas).

Aplicarán los norma argumentos en contra de la optimización prematura, pero no creo que realmente pierdes mucha claridad/sencillez mediante el uso de re.compile si usted sospecha que su regexps puede convertirse en un cuello de botella de rendimiento.

22voto

david king Puntos 316

Esto es un simple caso de prueba:

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

Así parece que compilar es más rápido con este caso simple, incluso si combinas sólo una vez.

6voto

George Puntos 945

He intentado esto mismo. Para el caso simple de analizar un número de una cadena y en síntesis, el uso de un compilado objeto de expresión regular es dos veces tan rápido como el uso de la re métodos.

Como otros han señalado, la re métodos (incluyendo re.compile) buscar la expresión regular de la cadena en un caché de compilado previamente expresiones. Por lo tanto, en el caso normal, el costo adicional de la utilización de la re métodos es simplemente el costo de la búsqueda en la caché.

Sin embargo, el examen del código, se muestra la caché está limitada a 100 expresiones. Esto plantea la pregunta, ¿qué tan doloroso es que desborde la memoria caché? El código contiene una interfaz interna para la expresión regular compilador, re.sre_compile.compile. Si nos llaman, nos omitir la caché. Resulta ser de alrededor de dos órdenes de magnitud más lento para una expresión regular, como r'\w+\s+([0-9_]+)\s+\w*'.

Aquí está mi prueba:

#!/usr/bin/env python
importación de re
tiempo de importación

def temporizado(func):
 def contenedor(*args):
 t = time.time()
 resultado = func(*args)
 t = time.time() - t
 print '%s tomó %.3f segundos.' % (func.func_name, t)
 resultado
 volver contenedor

regularExpression = r'\w+\s+([0-9_]+)\s+\w*'
testString = "promedio 2 nunca"

@temporizado
def noncompiled():
 a = 0
 for x in xrange(1000000):
 m = re.match(regularExpression, testString)
 a += int(m.group(1))
 devolver un

@temporizado
def compilado():
 a = 0
 rgx = re.compile(regularExpression)
 for x in xrange(1000000):
 m = rgx.match(testString)
 a += int(m.group(1))
 devolver un

@temporizado
def reallyCompiled():
 a = 0
 rgx = re.sre_compile.compile(regularExpression)
 for x in xrange(1000000):
 m = rgx.match(testString)
 a += int(m.group(1))
 devolver un


@temporizado
def compiledInLoop():
 a = 0
 for x in xrange(1000000):
 rgx = re.compile(regularExpression)
 m = rgx.match(testString)
 a += int(m.group(1))
 devolver un

@temporizado
def reallyCompiledInLoop():
 a = 0
 for x in xrange(10000):
 rgx = re.sre_compile.compile(regularExpression)
 m = rgx.match(testString)
 a += int(m.group(1))
 devolver un

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

Y aquí está el resultado de mi máquina:

$ regexTest.py 
noncompiled tomó 4.555 segundos.
compilado tomó 2.323 segundos.
reallyCompiled tomó 2.325 segundos.
compiledInLoop tomó 4.620 segundos.
reallyCompiledInLoop tomó 4.074 segundos.
r1 = 2000000
r2 = 2000000
r3 = 2000000
r4 = 2000000
r5 = 20000

El 'reallyCompiled' uso de los métodos de la interfaz interna, lo que evita la caché. Nota el que se compila en cada iteración del bucle es sólo afirmar 10.000 veces, no un millón.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: