pattern - re.sub python ejemplos
¿Vale la pena usar re.compile de Python? (22)
¿Hay algún beneficio en el uso de compilar para expresiones regulares en Python?
h = re.compile(''hello'')
h.match(''hello world'')
vs
re.match(''hello'', ''hello world'')
Tengo mucha experiencia ejecutando una expresión regular compilada miles de veces en lugar de compilar sobre la marcha, y no he notado ninguna diferencia perceptible
Los votos sobre la respuesta aceptada llevan a suponer que lo que dice @Triptych es cierto para todos los casos. Esto no necesariamente es cierto. Una gran diferencia es cuando tienes que decidir si aceptar una cadena de expresiones regulares o un objeto de expresiones regulares compilado como un parámetro para una función:
>>> 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
Siempre es mejor compilar sus expresiones regulares en caso de que necesite reutilizarlos.
Tenga en cuenta que el ejemplo en el tiempo anterior simula la creación de un objeto de expresión regular compilado una vez en el momento de la importación en lugar de "sobre la marcha" cuando se requiere para una coincidencia.
Acabo de intentar esto yo mismo. Para el caso simple de analizar un número de una cadena y sumarlo, usar un objeto de expresión regular compilado es casi el doble de rápido que usar los métodos re
.
Como han señalado otros, los métodos re
(incluido re.compile
) buscan la cadena de expresión regular en un caché de expresiones compiladas previamente. Por lo tanto, en el caso normal, el costo adicional de usar los métodos re
es simplemente el costo de la búsqueda de caché.
Sin embargo, el examen del code muestra que el caché está limitado a 100 expresiones. Esto plantea la pregunta, ¿qué tan doloroso es desbordar el caché? El código contiene una interfaz interna para el compilador de expresiones regulares, re.sre_compile.compile
. Si lo llamamos, saltamos el caché. Resulta aproximadamente dos órdenes de magnitud más lento que una expresión regular básica, como r''/w+/s+([0-9_]+)/s+/w*''
.
Aquí está mi prueba:
#!/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
Los métodos ''realmente compilados'' usan la interfaz interna, que omite la memoria caché. Tenga en cuenta que el que se compila en cada iteración de bucle solo se itera 10.000 veces, no un millón.
Además de la actuación.
El uso de compile
me ayuda a distinguir los conceptos de
1. módulo (re) ,
2. objeto regex
3. hacer coincidir el objeto
Cuando empecé a aprender regex
#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''
Como complemento, hice una hoja de trucos exhaustiva del módulo para su referencia.
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'',]
}
Aquí hay un caso de prueba 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
con 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
Por lo tanto, parece que la compilación es más rápida con este caso simple, incluso si solo haces coincidencias una vez .
Corrí esta prueba antes de encontrar la discusión aquí. Sin embargo, habiéndolo ejecutado, pensé que al menos publicaría mis resultados.
Robé y bastardé el ejemplo en "Mastering Regular Expressions" de Jeff Friedl. Esto es en un macbook con OSX 10.6 (2GHz intel core 2 duo, 4GB de RAM). La versión de Python es 2.6.1.
Ejecutar 1 - usando 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
Ejecutar 2 - No usar re.compile
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
Curiosamente, la compilación es más eficiente para mí (Python 2.5.2 en Win XP):
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
Ejecutando el código anterior una vez como está, y una vez con las dos if
líneas comentaron al revés, la expresión regular compilada es el doble de rápida
Dejando a un lado la diferencia de rendimiento, el uso de re.compile y el objeto compilado de expresiones regulares para hacer coincidir (independientemente de las operaciones relacionadas con expresiones regulares) hace que la semántica sea más clara respecto del tiempo de ejecución de Python.
Tuve una experiencia dolorosa de depurar un código simple:
compare = lambda s, p: re.match(p, s)
y luego usaría comparar en
[x for x in data if compare(patternPhrases, x[columnIndex])]
donde se supone que patternPhrases
es una variable que contiene una cadena de expresión regular, x[columnIndex]
es una variable que contiene una cadena.
¡Tuve problemas para que las patternPhrases
no coincidieran con alguna cadena esperada!
Pero si uso el formulario re.compile:
compare = lambda s, p: p.match(s)
entonces en
[x for x in data if compare(patternPhrases, x[columnIndex])]
Python se habría quejado de que "la cadena no tiene un atributo de coincidencia", como por el mapeo de argumentos posicionales en compare
, ¡ x[columnIndex]
se usa como expresión regular !, cuando en realidad quise decir
compare = lambda p, s: p.match(s)
En mi caso, el uso de re.compile es más explícito del propósito de la expresión regular, cuando su valor está oculto a simple vista, por lo que podría obtener más ayuda de la verificación en tiempo de ejecución de Python.
Entonces, la moraleja de mi lección es que cuando la expresión regular no es solo una cadena literal, entonces debería usar re.compile para que Python me ayude a afirmar mi suposición.
En general, me parece que es más fácil usar banderas (al menos más fácil de recordar cómo), como re.I
al compilar patrones que usar banderas en línea.
>>> 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'']
Esta es una buena pregunta. A menudo ves que la gente usa re.compile sin razón. Disminuye la legibilidad. Pero seguro que hay muchas veces en que se requiere la precompilación de la expresión. Como cuando lo usas repetidas veces en un bucle o algo así.
Es como todo sobre la programación (todo en la vida en realidad). Aplicar el sentido común.
Esta respuesta puede llegar tarde pero es un hallazgo interesante. Usar compilación puede realmente ahorrarle tiempo si planea usar la expresión regular varias veces (esto también se menciona en los documentos). A continuación puede ver que el uso de un regex compilado es más rápido cuando se llama directamente al método de coincidencia. pasar una expresión regular compilada a re.match la hace aún más lenta y pasar re.match con la cadena de patrones está en algún lugar en el medio.
>>> 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
Estoy de acuerdo con Honest Abe en que la match(...)
en los ejemplos dados es diferente. No son comparaciones de uno a uno y, por lo tanto, los resultados varían. Para simplificar mi respuesta, uso A, B, C, D para esas funciones en cuestión. Oh sí, estamos tratando con 4 funciones en re.py
en lugar de 3.
Ejecutando esta pieza de código:
h = re.compile(''hello'') # (A)
h.match(''hello world'') # (B)
es lo mismo que ejecutar este código:
re.match(''hello'', ''hello world'') # (C)
Porque, cuando se mira en la fuente re.py
, (A + B) significa:
h = re._compile(''hello'') # (D)
h.match(''hello world'')
y (C) es en realidad:
re._compile(''hello'').match(''hello world'')
Entonces, (C) no es lo mismo que (B). De hecho, (C) llama (B) después de llamar (D), que también es llamada por (A). En otras palabras, (C) = (A) + (B)
. Por lo tanto, comparar (A + B) dentro de un bucle tiene el mismo resultado que (C) dentro de un bucle.
La regexTest.py
de regexTest.py
de George demostró esto para nosotros.
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
El interés de todos es cómo obtener el resultado de 2.323 segundos. Para asegurarnos de que compile(...)
solo sea llamado una vez, necesitamos almacenar el objeto regex compilado en la memoria. Si estamos utilizando una clase, podríamos almacenar el objeto y reutilizarlo cada vez que se llame a nuestra función.
class Foo:
regex = re.compile(''hello'')
def my_function(text)
return regex.match(text)
Si no estamos utilizando la clase (que es mi solicitud de hoy), entonces no tengo ningún comentario. Todavía estoy aprendiendo a usar la variable global en Python, y sé que la variable global es algo malo.
Un punto más, creo que el uso de (A) + (B)
tiene una ventaja. Aquí hay algunos datos que observé (corríjame si me equivoco):
Llamadas A una vez, hará una búsqueda en el
_cache
seguido de unasre_compile.compile()
para crear un objeto de expresión regular. Llamadas A dos veces, hará dos búsquedas y una compilación (porque el objeto regex está en caché).Si el
_cache
se vacía, el objeto de expresión regular se libera de la memoria y Python necesita compilar nuevamente. (Alguien sugiere que Python no compilará).Si mantenemos el objeto de expresión regular utilizando (A), el objeto de expresión regular seguirá ingresando en _cache y se vaciará de alguna manera. Pero nuestro código mantiene una referencia en él y el objeto regex no se liberará de la memoria. Esos, Python no necesitan compilar de nuevo.
Las diferencias de 2 segundos en la prueba de George compiledInLoop vs compiled es principalmente el tiempo requerido para construir la clave y buscar el _cache. No significa el tiempo de compilación de expresiones regulares.
La prueba de realmente compilación de George muestra qué sucede si realmente vuelve a hacer la compilación cada vez: será 100 veces más lenta (redujo el bucle de 1.000.000 a 10.000).
Aquí están los únicos casos que (A + B) es mejor que (C):
- Si podemos almacenar en caché una referencia del objeto regex dentro de una clase.
- Si necesitamos llamar a (B) repetidamente (dentro de un bucle o varias veces), debemos almacenar en caché la referencia al objeto regex fuera del bucle.
Caso que (C) es suficientemente bueno:
- No podemos cachear una referencia.
- Solo lo usamos de vez en cuando.
- En general, no tenemos demasiadas expresiones regulares (supongamos que el compilado nunca se enjuaga)
Solo un resumen, aquí está el ABC:
h = re.compile(''hello'') # (A)
h.match(''hello world'') # (B)
re.match(''hello'', ''hello world'') # (C)
Gracias por leer.
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
Por lo tanto, si va a utilizar mucho la misma expresión regular, puede valer la pena hacer re.compile
(especialmente para expresiones regulares más complejas).
Se aplican los argumentos estándar contra la optimización prematura, pero no creo que pierda mucha claridad / re.compile
usar re.compile
si sospecha que sus expresiones regulares pueden convertirse en un cuello de botella en el rendimiento.
Actualizar:
Bajo Python 3.6 (sospecho que los tiempos anteriores se realizaron con Python 2.x) y el hardware 2018 (MacBook Pro), ahora obtengo los siguientes tiempos:
% 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.
También agregué un caso (observe las diferencias de comillas entre las dos últimas ejecuciones) que muestra que re.match(x, ...)
es literalmente [aproximadamente] equivalente a re.compile(x).match(...)
, es decir, no parece ocurrir ningún almacenamiento en caché entre bastidores de la representación compilada.
Hay una ventaja adicional de usar re.compile (), en la forma de agregar comentarios a mis patrones de expresiones regulares usando re.VERBOSE
pattern = ''''''
hello[ ]world # Some info on my pattern logic. [ ] to recognize space
''''''
re.search(pattern, ''hello world'', re.VERBOSE)
Aunque esto no afecta la velocidad de ejecución de su código, me gusta hacerlo de esta manera, ya que es parte de mi hábito de comentar. No me gusta pasar el tiempo tratando de recordar la lógica que quedó detrás de mi código 2 meses después cuando quiero hacer modificaciones.
He tenido mucha experiencia ejecutando una expresión regular compilada miles de veces en lugar de compilar sobre la marcha, y no he notado ninguna diferencia perceptible. Obviamente, esto es anecdótico, y ciertamente no es un gran argumento en contra de la compilación, pero he encontrado que la diferencia es insignificante.
EDITAR: Después de echar un vistazo rápido al código real de la biblioteca de Python 2.5, veo que Python compila internamente AND CACHES regexes cada vez que los usa (incluidas las llamadas a re.match()
), por lo que solo está cambiando CUANDO el regex obtiene compilado, y no debería ahorrar mucho tiempo en absoluto, solo el tiempo que lleva comprobar el caché (una búsqueda clave en un tipo de dict
interno).
Desde el módulo re.py (los comentarios son míos):
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 compilando con frecuencia expresiones regulares, pero solo para vincularlas a un nombre agradable y reutilizable, no para ninguna ganancia de rendimiento esperada.
Para mí, el mayor beneficio de re.compile
no es ningún tipo de optimización prematura (que es la raíz de todo mal , de anyway ). Es poder separar la definición de la expresión regular de su uso.
Incluso una expresión simple como 0|[1-9][0-9]*
(entero en la base 10 sin ceros iniciales) puede ser lo suficientemente compleja como para que no tenga que volver a escribirla, verifique si realizó algún error tipográfico, y luego tendrá que volver a verificar si hay errores tipográficos al iniciar la depuración. Además, es mejor usar un nombre de variable como num o num_b10 que 0|[1-9][0-9]*
.
Ciertamente es posible almacenar cadenas y pasarlas a re.match; Sin embargo, eso es menos legible:
num = "..."
# then, much later:
m = re.match(num, input)
Versus compilando:
num = re.compile("...")
# then, much later:
m = num.match(input)
Aunque está bastante cerca, la última línea de la segunda se siente más natural y más simple cuando se usa repetidamente.
Sobre todo, hay poca diferencia si usas re.compile o no. Internamente, todas las funciones se implementan en términos de un paso de compilación:
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)
Además, re.compile () omite la lógica adicional de indirección y almacenamiento en caché:
_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
Además del pequeño beneficio de la velocidad del uso de re.compile , a las personas también les gusta la legibilidad que se obtiene al nombrar especificaciones de patrones potencialmente complejos y separarlas de la lógica de negocios donde se aplican:
#### 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()
Tenga en cuenta que otro encuestado creyó incorrectamente que los archivos pyc almacenaban patrones compilados directamente; sin embargo, en realidad se reconstruyen cada vez que se carga el 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
El desmontaje anterior proviene del archivo PYC para un tmp.py
contiene:
import re
lc_vowels = re.compile(r''[aeiou]{2,5}'')
Usando los ejemplos dados:
h = re.compile(''hello'')
h.match(''hello world'')
El método de coincidencia en el ejemplo anterior no es el mismo que se usa a continuación:
re.match(''hello'', ''hello world'')
re.compile() devuelve un objeto de expresión regular , lo que significa que h
es un objeto regex.
El objeto regex tiene su propio método de match con los parámetros pos y endpos opcionales:
pos
El segundo parámetro opcional pos da un índice en la cadena donde se debe iniciar la búsqueda; su valor predeterminado es 0. Esto no es completamente equivalente a cortar la cadena; el carácter del patrón
''^''
coincide con el comienzo real de la cadena y en las posiciones justo después de una nueva línea, pero no necesariamente en el índice donde se inicia la búsqueda.
endpos
El parámetro opcional endpos limita la distancia a la que se buscará la cadena; será como si la cadena tuviera una longitud de caracteres de endpos , por lo que solo se buscará una coincidencia en los caracteres de pos a
endpos - 1
. Si endpos es menor que pos , no se encontrará ninguna coincidencia; de lo contrario, si rx es un objeto de expresión regular compilado,rx.search(string, 0, 50)
es equivalente arx.search(string[:50], 0)
.
Los métodos de búsqueda , búsqueda y búsqueda de objetos de expresiones regulares también admiten estos parámetros.
re.match(pattern, string, flags=0)
no los admite como puede ver,
ni tampoco sus contrapartes de búsqueda , búsqueda y búsqueda .
Un objeto coincidente tiene atributos que complementan estos parámetros:
match.pos
El valor de pos que se pasó al método search () o match () de un objeto regex. Este es el índice en la cadena en la que el motor RE comenzó a buscar una coincidencia.
match.endpos
El valor de endpos que se pasó al método search () o match () de un objeto regex. Este es el índice en la cadena más allá del cual el motor RE no irá.
Un objeto regex tiene dos atributos únicos, posiblemente útiles:
regex.groups
El número de grupos de captura en el patrón.
regex.groupindex
Un diccionario que mapea cualquier nombre de grupo simbólico definido por (? P) para agrupar números. El diccionario está vacío si no se usaron grupos simbólicos en el patrón.
Y finalmente, un objeto de coincidencia tiene este atributo:
match.re
El objeto de expresión regular cuyo método match () o search () produjo esta instancia de coincidencia.
(Meses después) es fácil agregar su propio caché alrededor de re.match, o cualquier otra cosa,
""" 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 ...
Un wibni, ¿no sería bueno si: cachehint (size =), cacheinfo () -> size, hits, nclear ...
Las expresiones regulares se compilan antes de usarse cuando se usa la segunda versión. Si lo vas a ejecutar muchas veces, es mejor compilarlo primero. Si no está compilando, cada vez que coincida con uno está bien.
Me gustaría motivar que la precompilación es tanto conceptual como ''literalmente'' (como en ''programación alfabetizada'') una ventaja. Echa un vistazo a este fragmento de código:
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()
en tu aplicación, escribirías:
from TYPO import TYPO
print( TYPO.text_has_foobar( ''FOObar ) )
Esto es casi tan simple en términos de funcionalidad como puede obtener. Debido a que este ejemplo es tan corto, combiné la manera de obtener _text_has_foobar_re_search
todo en una línea. la desventaja de este código es que ocupa un poco de memoria por lo que sea la vida útil del TYPO
objeto de la biblioteca; La ventaja es que al hacer una búsqueda en foobar, saldrás con dos llamadas de función y dos búsquedas de diccionario de clase. la cantidad de expresiones regulares que se almacenan en caché re
y la sobrecarga de esa memoria caché son irrelevantes aquí.
Compara esto con el estilo más habitual, a continuación:
import re
class Typo:
def text_has_foobar( self, text ):
return re.compile( r"""(?i)foobar""" ).search( text ) is not None
En la aplicacion:
typo = Typo()
print( typo.text_has_foobar( ''FOObar ) )
Admito fácilmente que mi estilo es muy inusual para python, tal vez incluso discutible. sin embargo, en el ejemplo que más se asemeja a cómo Python se usa principalmente, para hacer una única coincidencia, debemos instanciar un objeto, hacer tres búsquedas de diccionario de instancia y realizar tres llamadas de función; Además, podríamos tener re
problemas de almacenamiento en caché cuando usamos más de 100 expresiones regulares. Además, la expresión regular se oculta dentro del cuerpo del método, que la mayoría de las veces no es una buena idea.
se dice que cada subconjunto de medidas --- declaraciones de importación dirigidas y con alias; métodos de alias cuando sea aplicable; la reducción de llamadas a funciones y búsquedas de diccionarios de objetos puede ayudar a reducir la complejidad computacional y conceptual.
Realmente respeto todas las respuestas anteriores. De mi opinión ¡Sí! Por supuesto, vale la pena usar re.compile en lugar de compilar la expresión regular, una y otra vez, cada vez.
Usar re.compile hace que su código sea más dinámico, ya que puede llamar a la expresión regular ya compilada, en lugar de volver a compilar y volver a compilar. Esto te beneficia en los casos:
- Esfuerzos del procesador
- Complejidad del tiempo.
- Hace regex Universal (puede ser usado en findall, search, match)
- Y hace que tu programa se vea bien.
Ejemplo:
example_string = "The room number of her room is 26A7B."
find_alpha_numeric_string = re.compile(r"/b/w+/b")
Usando en Findall
find_alpha_numeric_string.findall(example_string)
Usando en la búsqueda
find_alpha_numeric_string.search(example_string)
Del mismo modo puedes usarlo para: Match y Substitute
Tengo entendido que esos dos ejemplos son efectivamente equivalentes. La única diferencia es que en la primera, puede reutilizar la expresión regular compilada en otro lugar sin que se vuelva a compilar.
Aquí hay una referencia para usted: http://diveintopython3.ep.io/refactoring.html
Llamar a la función de búsqueda del objeto de patrón compilado con la cadena ''M'' logra lo mismo que llamar a re.search tanto con la expresión regular como con la cadena ''M''. Sólo mucho, mucho más rápido. (De hecho, la función re.search simplemente compila la expresión regular y llama el método de búsqueda del objeto del patrón resultante).