2008-09-28 13 views
7

Mi objetivo aquí es crear un lenguaje de plantilla muy simple. Por el momento, estoy trabajando en la sustitución de una variable con un valor, como este:Estoy usando expresiones regulares de Python de manera criminalmente ineficiente

Esta entrada:

La web

debe producir este resultado:

El Web Esta es una variable de prueba

Lo tengo trabajando. Pero mirando mi código, estoy ejecutando múltiples expresiones regulares idénticas en las mismas cadenas, eso solo ofende mi sentido de la eficiencia. Tiene que haber una forma mejor, más Pythonic. (Son los dos bucles "while" los que realmente ofenden)

Esto pasa las pruebas unitarias, así que si esto es una optimización tonta prematura, dígame: estoy dispuesto a dejar esto. Puede haber docenas de estas definiciones de variables y usos en un documento, pero no en cientos. Pero sospecho que hay maneras obvias (para otras personas) de mejorar esto, y tengo curiosidad por saber qué se le ocurrirá a la multitud de StackOverflow.

def stripMatchedQuotes(item): 
    MatchedSingleQuotes = re.compile(r"'(.*)'", re.LOCALE) 
    MatchedDoubleQuotes = re.compile(r'"(.*)"', re.LOCALE) 
    item = MatchedSingleQuotes.sub(r'\1', item, 1) 
    item = MatchedDoubleQuotes.sub(r'\1', item, 1) 
    return item 




def processVariables(item): 
    VariableDefinition = re.compile(r'<%(.*?)=(.*?)%>', re.LOCALE) 
    VariableUse = re.compile(r'<%(.*?)%>', re.LOCALE) 
    Variables={} 

    while VariableDefinition.search(item): 
     VarName, VarDef = VariableDefinition.search(item).groups() 
     VarName = stripMatchedQuotes(VarName).upper().strip() 
     VarDef = stripMatchedQuotes(VarDef.strip()) 
     Variables[VarName] = VarDef 
     item = VariableDefinition.sub('', item, 1) 

    while VariableUse.search(item): 
     VarName = stripMatchedQuotes(VariableUse.search(item).group(1).upper()).strip() 
     item = VariableUse.sub(Variables[VarName], item, 1) 

    return item 
+0

El mundo no necesita otro lenguaje de plantilla de Python. Ya existen docenas. ¿No puedes usar uno de esos? –

+0

@Schof: te puedo mencionar en http://genshi.edgewall.org/, que hará de tu vida un pastel. – user7116

+1

Me encanta el Título +1 por hacerme reír. – UnkwnTech

Respuesta

10

Lo primero que puede mejorar las cosas es mover el re.compile fuera de la función. La compilación está almacenada en caché, pero se acelera al verificar esto para ver si está compilada.

Otra posibilidad es utilizar una sola expresión regular de la siguiente manera:

MatchedQuotes = re.compile(r"(['\"])(.*)\1", re.LOCALE) 
item = MatchedQuotes.sub(r'\2', item, 1) 

Por último, se puede combinar esto en la expresión regular en processVariables. Al tomar la sugerencia de Torsten Marek's de usar una función para re.sub, esto mejora y simplifica las cosas de manera espectacular.

VariableDefinition = re.compile(r'<%(["\']?)(.*?)\1=(["\']?)(.*?)\3%>', re.LOCALE) 
VarRepl = re.compile(r'<%(["\']?)(.*?)\1%>', re.LOCALE) 

def processVariables(item): 
    vars = {} 
    def findVars(m): 
     vars[m.group(2).upper()] = m.group(4) 
     return "" 

    item = VariableDefinition.sub(findVars, item) 
    return VarRepl.sub(lambda m: vars[m.group(2).upper()], item) 

print processVariables('<%"TITLE"="This Is A Test Variable"%>The Web <%"TITLE"%>') 

Aquí están mis tiempos de carreras 100000:

Original  : 13.637 
Global regexes : 12.771 
Single regex : 9.095 
Final version : 1.846 

[Editar] Añadir faltante especificador no expansivo

[Edit2] Añadido.Las llamadas superiores() no distinguen entre mayúsculas y minúsculas, como la versión original

+0

¡Buen trabajo, recibe mi voto! –

2

Nunca cree su propio lenguaje de programación. Nunca. (Solía ​​tener una excepción a esta regla, pero ya no más).

Siempre hay un idioma existente que puede usar que se adapte mejor a sus necesidades. Si amplió su caso de uso, las personas pueden ayudarlo a seleccionar un idioma adecuado.

+0

La creación de un lenguaje específico del dominio es una acción perfectamente ACEPTABLE, si es apropiado. Nunca digas nunca. – skaffman

+0

Eso puede ser, pero ya hay un trillón de buenas opciones para lenguajes de plantillas de texto en Python ... –

+4

Parte de la razón por la que estoy haciendo esto es para aumentar mis habilidades. Si esto era algo por lo que me pagaban, seguiría su sugerencia y lo haría de la manera más rápida.Como estoy tratando de maximizar el aprendizaje, no la eficiencia del programador. – Schof

1

Puede hacer coincidir ambos tipos de citas de una sola vez con r"(\"|')(.*?)\1" - el \1 se refiere al primer grupo, por lo que solo coincidirá con las comillas coincidentes.

0

No llame a la búsqueda dos veces seguidas (en el bucle condicional y la primera instrucción en el bucle). Llame (y guarde en caché el resultado) una vez antes del ciclo, y luego en la declaración final del ciclo.

1

Llamas a re.compile un poco. Una variable global para estos no estaría mal aquí.

3

sub pueden tomar un argumento invocable en lugar de una cadena simple. El uso de eso, puede reemplazar todas las variables con una llamada función:

>>> import re 
>>> var_matcher = re.compile(r'<%(.*?)%>', re.LOCALE) 
>>> string = '<%"TITLE"%> <%"SHMITLE"%>' 
>>> values = {'"TITLE"': "I am a title.", '"SHMITLE"': "And I am a shmitle."} 
>>> var_matcher.sub(lambda m: vars[m.group(1)], string) 
'I am a title. And I am a shmitle. 

seguir el consejo de eduffy.myopenid.com y mantener las expresiones regulares compiladas alrededor.

La misma receta se puede aplicar al primer bucle, solo allí debe almacenar primero el valor de la variable y siempre devolver "" como reemplazo.

1

Si una expresión regular solo contiene un. * Comodín y literales, entonces puede usar find y rfind para localizar los delimitadores de apertura y cierre.

Si contiene solo una serie de. *? comodines y literales, entonces puedes usar una serie de hallazgos para hacer el trabajo.

Si el código es de tiempo crítico, este cambio lejos de las expresiones regulares podría dar un poco más de velocidad.

Además, me parece que este es un LL-parsable language. Puede buscar una biblioteca que ya pueda analizar esas cosas por usted. También podría usar llamadas recursivas para hacer un análisis de una sola pasada; por ejemplo, podría implementar su función processVariables para consumir solo la primera cotización, y luego llamar a una función de ajuste de presupuesto para consumir hasta la siguiente cotización, etc.

2

Crear un lenguaje de plantillas está muy bien, pero ¿no debería uno de los objetivos del lenguaje de plantillas ser de fácil lectura y análisis eficiente? El ejemplo que diste parece no ser ninguno de los dos.

Como Jamie Zawinsky dijo la famosa frase:

Algunas personas, cuando se enfrentan a un problema , piense "Yo sé, voy a usar expresiones regulares !" Ahora tienen dos problemas.

Si las expresiones regulares son una solución a un problema que ha creado, la mejor opción es no escribir una expresión regular mejor, sino rediseñar su enfoque para eliminar su uso por completo. Las expresiones regulares son complicadas, costosas, muy difíciles de mantener e (idealmente) solo deberían usarse para solucionar un problema que alguien más creó.

+0

En principio, estoy de acuerdo. Deberíamos ir a la voz de usuario y obligarlos a mostrar esta cita cada vez que alguien escribe una pregunta y le da la etiqueta "regex". –

+0

Tengo curiosidad de por qué crees que el ejemplo no tiene facilidad de lectura o un análisis eficiente. ¿Qué podría cambiar para que sea más legible y más fácil de analizar? – Schof

-3

¿Por qué no usar XML y XSLT en lugar de crear su propio lenguaje de plantilla? Lo que quieres hacer es bastante fácil en XSLT.

1

¿Por qué no utilizar Mako? Seriamente. ¿Qué función necesitas que Mako no tenga? Quizás puedas adaptar o ampliar algo que ya funciona.

Cuestiones relacionadas