pattern - re.sub python ejemplos
Python''s re module-¿estado de guardado? (6)
Es posible que le guste este módulo que implementa el contenedor que está buscando.
Una de las mayores molestias que encuentro en Python es la incapacidad del módulo re
para guardar su estado sin hacerlo explícitamente en un objeto coincidente. A menudo, uno necesita analizar las líneas y si cumplen una cierta expresión regular extraer los valores de ellos por la misma expresión regular. Me gustaría escribir un código como este:
if re.match(''foo (/w+) bar (/d+)'', line):
# do stuff with .group(1) and .group(2)
elif re.match(''baz whoo_(/d+)'', line):
# do stuff with .group(1)
# etc.
Pero desafortunadamente es imposible llegar al objeto coincidente de la llamada anterior a re.match
, por lo que esto se escribe así:
m = re.match(''foo (/w+) bar (/d+)'', line)
if m:
# do stuff with m.group(1) and m.group(2)
else:
m = re.match(''baz whoo_(/d+)'', line)
if m:
# do stuff with m.group(1)
Lo cual es bastante menos conveniente y se vuelve muy difícil de manejar a medida que la lista de elif
crece.
Una solución hackosa sería ajustar el re.match y re.search en mis propios objetos que mantienen el estado en alguna parte. Alguien ha usado esto? ¿Conoce las implementaciones semi estándar (en marcos grandes o algo así)?
¿Qué otras soluciones alternativas puede recomendar? O tal vez, ¿estoy haciendo un mal uso del módulo y podría satisfacer mis necesidades de una manera más limpia?
Gracias por adelantado
Podría escribir una clase de utilidad para realizar la operación "guardar estado y devolver resultado". No creo que esto sea tan hackish. Es bastante trivial de implementar:
class Var(object):
def __init__(self, val=None): self.val = val
def set(self, result):
self.val = result
return result
Y luego usarlo como:
lastMatch = Var()
if lastMatch.set(re.match(''foo (/w+) bar (/d+)'', line)):
print lastMatch.val.groups()
elif lastMatch.set(re.match(''baz whoo_(/d+)'', line)):
print lastMatch.val.groups()
Probando algunas ideas ...
Parece que idealmente querrías una expresión con efectos secundarios. Si esto estuviera permitido en Python:
if m = re.match(''foo (/w+) bar (/d+)'', line):
# do stuff with m.group(1) and m.group(2)
elif m = re.match(''baz whoo_(/d+)'', line):
# do stuff with m.group(1)
elif ...
... entonces estarás clara y limpiamente expresando tu intención. Pero no lo es. Si se permitieran los efectos secundarios en las funciones anidadas, podría:
m = None
def assign_m(x):
m = x
return x
if assign_m(re.match(''foo (/w+) bar (/d+)'', line)):
# do stuff with m.group(1) and m.group(2)
elif assign_m(re.match(''baz whoo_(/d+)'', line)):
# do stuff with m.group(1)
elif ...
Ahora, no solo eso se está poniendo feo, sino que aún no es un código de Python válido: la función anidada ''assign_m'' no puede modificar la variable m
en el ámbito externo. Lo mejor que se me ocurre es realmente feo, utilizando una clase anidada a la que se le permiten los efectos secundarios:
# per Brian''s suggestion, a wrapper that is stateful
class m_(object):
def match(self, *args):
self.inner_ = re.match(*args)
return self.inner_
def group(self, *args):
return self.inner_.group(*args)
m = m_()
# now ''m'' is a stateful regex
if m.match(''foo (/w+) bar (/d+)'', line):
# do stuff with m.group(1) and m.group(2)
elif m.match(''baz whoo_(/d+)'', line):
# do stuff with m.group(1)
elif ...
Pero eso es claramente excesivo.
Considere la posibilidad de utilizar una función interna para permitir las salidas de alcance local, lo que le permite eliminar la anidación else
:
def find_the_right_match():
# now ''m'' is a stateful regex
m = re.match(''foo (/w+) bar (/d+)'', line)
if m:
# do stuff with m.group(1) and m.group(2)
return # <== exit nested function only
m = re.match(''baz whoo_(/d+)'', line)
if m:
# do stuff with m.group(1)
return
find_the_right_match()
Esto le permite aplanar nesting = (2 * N-1) a nesting = 1, pero es posible que haya movido el problema de los efectos secundarios, y las funciones anidadas pueden confundir a la mayoría de los programadores de Python.
Por último, hay formas libres de efectos secundarios para lidiar con esto:
def cond_with(*phrases):
"""for each 2-tuple, invokes first item. the first pair where
the first item returns logical true, result is passed to second
function in pair. Like an if-elif-elif.. chain"""
for (cond_lambda, then_lambda) in phrases:
c = cond_lambda()
if c:
return then_lambda(c)
return None
cond_with(
((lambda: re.match(''foo (/w+) bar (/d+)'', line)),
(lambda m:
... # do stuff with m.group(1) and m.group(2)
)),
((lambda: re.match(''baz whoo_(/d+)'', line)),
(lambda m:
... # do stuff with m.group(1)
)),
...)
Y ahora el código apenas se parece a Python, y mucho menos comprensible para los programadores de Python (¿es Lisp?).
Creo que la moraleja de esta historia es que Python no está optimizado para este tipo de expresiones idiomáticas. Realmente necesita ser un poco detallado y vivir con un gran factor de anidación de otras condiciones.
class last(object):
def __init__(self, wrapped, initial=None):
self.last = initial
self.func = wrapped
def __call__(self, *args, **kwds):
self.last = self.func(*args, **kwds)
return self.last
def test():
"""
>>> test()
crude, but effective: (oYo)
"""
import re
m = last(re.compile("(oYo)").match)
if m("abc"):
print("oops")
elif m("oYo"): #A
print("crude, but effective: (%s)" % m.last.group(1)) #B
else:
print("mark")
if __name__ == "__main__":
import doctest
doctest.testmod()
last
también es adecuado como decorador.
Me di cuenta de que en mi esfuerzo por hacer autoverificaciones y trabajar en 2.5, 2.6 y 3.0, he oscurecido algo la solución real. Las líneas importantes están marcadas como #A y #B arriba, donde usa el mismo objeto para probar ( is_somename
match
o is_somename
) y recupere su último valor. Fácil de abusar, pero también es fácil de ajustar y, si no se empuja demasiado, obtiene un código sorprendentemente claro.
En base a las excelentes respuestas a esta pregunta, he creado el siguiente mecanismo. Parece una forma general de resolver la limitación de "no asignación en condiciones" de Python. El enfoque es la transparencia, implementado por la delegación silenciosa:
class Var(object):
def __init__(self, val=None):
self._val = val
def __getattr__(self, attr):
return getattr(self._val, attr)
def __call__(self, arg):
self._val = arg
return self._val
if __name__ == "__main__":
import re
var = Var()
line = ''foo kwa bar 12''
if var(re.match(''foo (/w+) bar (/d+)'', line)):
print var.group(1), var.group(2)
elif var(re.match(''baz whoo_(/d+)'', line)):
print var.group(1)
En el caso general, esta es una solución segura para subprocesos, porque puede crear sus propias instancias de Var
. Para una mayor facilidad de uso cuando el enhebrado no es un problema, se puede importar y usar un objeto Var predeterminado. Aquí hay un módulo que contiene la clase Var:
class Var(object):
def __init__(self, val=None):
self._val = val
def __getattr__(self, attr):
return getattr(self._val, attr)
def __call__(self, arg):
self._val = arg
return self._val
var = Var()
Y aquí está el código del usuario:
from var import Var, var
import re
line = ''foo kwa bar 12''
if var(re.match(''foo (/w+) bar (/d+)'', line)):
print var.group(1), var.group(2)
elif var(re.match(''baz whoo_(/d+)'', line)):
print var.group(1)
Aunque no es seguro para subprocesos, para muchos scripts simples esto proporciona un atajo útil.
Probablemente la solución más simple es regresar temprano para que pueda volver a crear variables en lugar de tener que hacer una prueba inmediata.
def get_results(line):
m = re.match(''foo (/w+) bar (/d+)'', line)
if m:
# do stuff with .group(1) and .group(2)
return result
m = re.match(''baz whoo_(/d+)'', line)
if m:
# do stuff with .group(1)
return other_result
# etc.
De esta forma evitará el exceso de anidación.