2012-03-28 17 views
6

Actualmente estoy escribiendo un script de Python para procesar aproximadamente unos 10.000 documentos de entrada. Basado en el resultado del progreso del script, noto que los primeros 400+ documentos se procesan muy rápido y luego el script se ralentiza aunque todos los documentos de entrada son aproximadamente del mismo tamaño.Manera pitónica y eficiente de definir expresiones regulares múltiples para su uso en muchas iteraciones

Supongo que esto puede tener que ver con el hecho de que la mayor parte del procesamiento de documentos se realiza con expresiones regulares que no guardo como objetos de expresiones regulares una vez que se han compilado. En cambio, recompilo las expresiones regulares cada vez que las necesito.

Dado que mi script tiene alrededor de 10 funciones diferentes, todas usan entre 10 y 20 patrones de expresiones regulares diferentes. Me pregunto cuál sería una forma más eficiente en Python para evitar volver a compilar los patrones de expresiones regulares una y otra vez (en Perl Podría simplemente incluir un modificador //o).

Mi suposición es que si almacenar los objetos de expresiones regulares en las funciones individuales utilizando

pattern = re.compile() 

el objeto regex resultante no será retenido hasta la siguiente invocación de la función para la siguiente iteración (cada función se llama pero una vez por documento).

Crear una lista global de expresiones regulares pre compiladas parece una opción poco atractiva ya que necesitaría almacenar la lista de expresiones regulares en una ubicación diferente en mi código que donde realmente se usan.

¿Alguna sugerencia aquí sobre cómo manejar esto de forma clara y eficiente?

+2

No, tiene que ver con el hecho de que * su caché está agotada *. –

+1

¿Ha perfilado su código? – Daenyth

+1

son todas las funciones aplicadas a todos los documentos? porque si es así, la respuesta de @larsmans, aunque es buena, no parece explicar la desaceleración después de 400 documentos. Sugeriría crear perfiles en lugar de adivinar ... –

Respuesta

9

El módulo re almacena en caché los patrones regex compilados. La memoria caché se borra cuando alcanza un tamaño de re._MAXCACHE que de forma predeterminada es 100. (Dado que tiene 10 funciones con 10-20 expresiones regulares cada una (es decir, 100-200 expresiones regulares), la desaceleración observada tiene sentido con la eliminación de . la memoria caché)

Si estás bien con el cambio de variables privadas, una solución rápida y sucia para su programa podría ser la de establecer re._MAXCACHE a un valor más alto:

import re 
re._MAXCACHE = 1000 
+0

Esto es genial ... gracias por esta pista. – Pat

2

Compilado expresión regular se almacenan en caché automáticamente por re.compile, re.search y re.match, pero el tamaño máximo de caché es 100 en Python 2.7, por lo que usted está desbordando la caché.

Crear una lista global de expresiones regulares pre compiladas parece una opción poco atractiva ya que necesitaría almacenar la lista de expresiones regulares en una ubicación diferente en mi código que donde realmente se usan.

Puede definirlos cerca del lugar donde se utilizan: justo antes de las funciones que los utilizan. Si reutilizas el mismo RE en un lugar diferente, entonces habría sido una buena idea definirlo globalmente de todos modos para evitar tener que modificarlo en varios lugares.

5

última vez que miré, re.compile mantuvo un caché bastante pequeño, y cuando se llenó, simplemente lo vació.DIY sin límite:

class MyRECache(object): 
    def __init__(self): 
     self.cache = {} 
    def compile(self, regex_string): 
     if regex_string not in self.cache: 
      self.cache[regex_string] = re.compile(regex_string) 
     return self.cache[regex_string] 
+5

O, aún más sucinto, [deriva de 'dict' y sobrescribe' __missing__() '] (https://gist.github.com/2230130). –

+1

@SvenMarnach: El código que escribí puede ser entendido por la persona sin la necesidad de buscar los documentos '__voodoo__'. –

+0

Sería interesante saber cómo se borra la caché cuando se agota su capacidad ... ¿se vacían todas las entradas o solo unas pocas? – Pat

1

En el espíritu de "simple es mejor" que haría uso de un poco de función auxiliar de esta manera:

def rc(pattern, flags=0): 
    key = pattern, flags 
    if key not in rc.cache: 
     rc.cache[key] = re.compile(pattern, flags) 
    return rc.cache[key] 

rc.cache = {} 

Uso:

rc('[a-z]').sub... 
rc('[a-z]').findall <- no compilation here 

también te recomiendo que pruebes regex. Entre muchas otras ventajas sobre el stock re, su MAXCACHE es 500 por defecto y no se eliminará completamente en caso de desbordamiento.

+0

Gracias a todos los que se molestaron en responder mi consulta. Haré un seguimiento de los muchos consejos útiles. ¡Su apoyo es muy apreciado!. – Pat

Cuestiones relacionadas