2009-07-20 11 views
29

Si desea comprobar si hay algo que coincide con una expresión regular, si es así, imprimir el primer grupo, lo hace ..Alternativa al `match = re.match(); si coinciden: ... 'idiom?

import re 
match = re.match("(\d+)g", "123g") 
if match is not None: 
    print match.group(1) 

Esto es completamente pedante, pero el match variable intermedia es un poco molesto ..

lenguajes como Perl hacen esto mediante la creación de nuevos $1 .. $9 variables para grupos de partidos, como ..

if($blah ~= /(\d+)g/){ 
    print $1 
} 

de this reddit comment,

with re_context.match('^blah', s) as match: 
    if match: 
     ... 
    else: 
     ... 

..which pensé que era una idea interesante, así que escribió una implementación sencilla de la misma:

#!/usr/bin/env python2.6 
import re 

class SRE_Match_Wrapper: 
    def __init__(self, match): 
     self.match = match 

    def __exit__(self, type, value, tb): 
     pass 

    def __enter__(self): 
     return self.match 

    def __getattr__(self, name): 
     if name == "__exit__": 
      return self.__exit__ 
     elif name == "__enter__": 
      return self.__name__ 
     else: 
      return getattr(self.match, name) 

def rematch(pattern, inp): 
    matcher = re.compile(pattern) 
    x = SRE_Match_Wrapper(matcher.match(inp)) 
    return x 
    return match 

if __name__ == '__main__': 
    # Example: 
    with rematch("(\d+)g", "123g") as m: 
     if m: 
      print(m.group(1)) 

    with rematch("(\d+)g", "123") as m: 
     if m: 
      print(m.group(1)) 

(Esta funcionalidad teóricamente podrían ser parcheadas en el objeto _sre.SRE_Match)

Se Sería bueno si pudiera saltear la ejecución del bloque de código de la instrucción with, si no hubiera coincidencia, lo que simplificaría esto a ...

with rematch("(\d+)g", "123") as m: 
    print(m.group(1)) # only executed if the match occurred 

..pero esto parece imposible basa de lo que puedo deducir de PEP 343

¿Alguna idea? Como dije, esto es realmente una molestia trivial, casi hasta el punto de ser código de golf.

+1

"casi hasta el punto de ser código de golf" no están de acuerdo. Esto es totalmente código de golf. No se puede ver qué es "molesto" sobre la configuración de una variable para representar el cambio de estado. –

+0

Dije que estaba siendo pedante ... – dbr

+2

Discusión similar ya en SO: http://stackoverflow.com/questions/447086/pythons-re-module-saving-state –

Respuesta

12

No creo que sea trivial. No quiero tener que rociar un condicional redundante alrededor de mi código si estoy escribiendo código así a menudo.

Esto es un poco extraño, pero se puede hacer esto con un iterador:

import re 

def rematch(pattern, inp): 
    matcher = re.compile(pattern) 
    matches = matcher.match(inp) 
    if matches: 
     yield matches 

if __name__ == '__main__': 
    for m in rematch("(\d+)g", "123g"): 
     print(m.group(1)) 

Lo curioso es que se trata de utilizar un iterador para algo que no está iterando - está más cerca de un condicional, y a primera vista, puede parecer que arrojará resultados múltiples para cada partido.

Parece extraño que un gestor de contexto no pueda omitir por completo su función gestionada; Si bien no es explícitamente uno de los casos de uso de "con", parece una extensión natural.

+0

Sí, funcionaría si el código '__enter__' fuera ejecutado dentro de la parte' try' que le da al código '__exit__' el control sobre las excepciones (porque el código' __enter__' podría arrojar un tipo especial de excepción que es absorbida por la declaración 'con ', que detiene efectivamente la ejecución de cualquier código que se encuentre dentro de ella.) En este momento, no veo una manera de evitarlo. – Blixt

+3

Sería bueno si Python permitiera la asignación en expresiones, como C: "si x = y():", "if not (x = y():"; se manejaría esto directamente. –

+0

+1: This es la única solución viable para guardar una línea de código.Si bien el uso de un generador no es muy intuitivo, hace el trabajo y es seguro tanto para el alcance como para el hilo. – Blixt

0

No creo que usar with sea la solución en este caso. Debería plantear una excepción en la parte BLOCK (especificada por el usuario) y hacer que el método __exit__ devuelva True para "tragar" la excepción. Entonces nunca se vería bien.

Sugiero ir por una sintaxis similar a la sintaxis de Perl. Haga su propio módulo extendido re (voy a llamarlo rex) y tienen que establecer variables en su módulo de espacio de nombres:

if rex.match('(\d+)g', '123g'): 
    print rex._1 

Como se puede ver en los comentarios, este método no es ni alcance- ni seguro para subprocesos . Solo usaría esto si estuviera completamente seguro de que su aplicación no se volvería multiproceso en el futuro y de que cualquier función llamada desde el alcance en el que está usando esto también usará el mismo método.

+1

Tenga cuidado con esto. Se romperá si el código dentro del condicional reutiliza el objeto "rex", por lo que para estar seguro en ese caso necesitaría hacer una copia cuando lo use, y eso es sobre todo deshaciendo el beneficio. Tampoco es seguro para los hilos a menos que saltes algunos aros TLS; match() en un objeto re (debería ser) completamente seguro de hilos. Creo que estos problemas superan con creces los beneficios de hacerlo de esta manera; es mucho tener que tener en cuenta para guardar una línea de código. –

+0

e hilo locales para manejar multi-threading? –

+0

En algunos casos creo que los beneficios superan los problemas. Para programas simples de un solo hilo, creo que este enfoque está bien. Sin embargo, "match = re.match (...); if match: ..." es idiomático python. Seguiré haciéndolo de esa manera yo mismo. Aún +1 a la respuesta de @ Blixt, para una respuesta elegante y tipo perl a la pregunta original. – codeape

0

Si estás haciendo una gran cantidad de ellos en un solo lugar, aquí es una respuesta alternativa:

import re 
class Matcher(object): 
    def __init__(self): 
     self.matches = None 
    def set(self, matches): 
     self.matches = matches 
    def __getattr__(self, name): 
     return getattr(self.matches, name) 

class re2(object): 
    def __init__(self, expr): 
     self.re = re.compile(expr) 

    def match(self, matcher, s): 
     matches = self.re.match(s) 
     matcher.set(matches) 
     return matches 

pattern = re2("(\d+)g") 
m = Matcher() 
if pattern.match(m, "123g"): 
    print(m.group(1)) 
if not pattern.match(m, "x123g"): 
    print "no match" 

Puede compilar la expresión regular una vez con el mismo hilo de seguridad como nuevo, crear un único objeto Matcher reutilizable para toda la función, y luego puedes usarla de manera muy concisa. Esto también tiene el beneficio de que puedes revertirlo de la manera obvia: para hacer eso con un iterador, necesitarías pasar una bandera para decirle que invierta su resultado.

No es de mucha ayuda si solo hace una sola coincidencia por función; no desea mantener los objetos de Matcher en un contexto más amplio que ese; causaría los mismos problemas que la solución de Blixt.

0

esto no es realmente bastante parecido, pero se puede sacar provecho de la función incorporada getattr(object, name[, default]) usarlo como esto:

>>> getattr(re.match("(\d+)g", "123g"), 'group', lambda n:'')(1) 
'123' 
>>> getattr(re.match("(\d+)g", "X23g"), 'group', lambda n:'')(1) 
'' 

Para imitar la si el grupo partido de impresión flujo, puede (ab) utilizar la instrucción for esta manera:

>>> for group in filter(None, [getattr(re.match("(\d+)g", "123g"), 'group', None)]): 
     print(group(1)) 
123 
>>> for group in filter(None, [getattr(re.match("(\d+)g", "X23g"), 'group', None)]): 
     print(group(1)) 
>>> 

Por supuesto, se puede definir una función poco a hacer el trabajo sucio:

>>> matchgroup = lambda p,s: filter(None, [getattr(re.match(p, s), 'group', None)]) 
>>> for group in matchgroup("(\d+)g", "123g"): 
     print(group(1)) 
123 
>>> for group in matchgroup("(\d+)g", "X23g"): 
     print(group(1)) 
>>> 
0
No

la solución perfecta, pero que le permitirá a la cadena de varias opciones de coincidencia para el mismo str:

class MatchWrapper(object): 
    def __init__(self): 
    self._matcher = None 

    def wrap(self, matcher): 
    self._matcher = matcher 

    def __getattr__(self, attr): 
    return getattr(self._matcher, attr) 

def match(pattern, s, matcher): 
    m = re.match(pattern, s) 
    if m: 
    matcher.wrap(m) 
    return True 
    else: 
    return False 

matcher = MatchWrapper() 
s = "123g"; 
if _match("(\d+)g", line, matcher): 
    print matcher.group(1) 
elif _match("(\w+)g", line, matcher): 
    print matcher.group(1) 
else: 
    print "no match" 
4

Otra sintaxis agradable sería algo como esto:

header = re.compile('(.*?) = (.*?)$') 
footer = re.compile('(.*?): (.*?)$') 

if header.match(line) as m: 
    key, value = m.group(1,2) 
elif footer.match(line) as m 
    key, value = m.group(1,2) 
else: 
    key, value = None, None 
1

tengo otro forma de hacerlo, en base a la solución de Glen Maynard:

for match in [m for m in [re.match(pattern,key)] if m]: 
    print "It matched: %s" % match 

Al igual que la solución de Glen, este itte califica 0 (si no coincide) o 1 (si coincide) veces.

Sin necesidad de subutilizar, pero menos ordenado.

0

aquí está mi solución:

import re 

s = 'hello world' 

match = [] 
if match.append(re.match('w\w+', s)) or any(match): 
    print('W:', match.pop().group(0)) 
elif match.append(re.match('h\w+', s)) or any(match): 
    print('H:', match.pop().group(0)) 
else: 
    print('No match found') 

Puede utilizar tantos elif cláusulas según sea necesario.

Aún mejor:

import re 

s = 'hello world' 

if vars().update(match=re.match('w\w+', s)) or match: 
    print('W:', match.group(0)) 
elif vars().update(match=re.match('h\w+', s)) or match: 
    print('H:', match.group(0)) 
else: 
    print('No match found') 

Tanto anexar y actualización retorno Ninguno. Por lo tanto, debe verificar el resultado de su expresión utilizando la parte o en todos los casos.

Desafortunadamente, esto solo funciona mientras el código resida en el nivel superior, es decir, no en una función.

0

Esto es lo que hago:

def re_match_cond (match_ref, regex, text): 
    match = regex.match (text) 
    del match_ref[:] 
    match_ref.append (match) 
    return match 

if __name__ == '__main__': 
    match_ref = [] 
    if re_match_cond (match_ref, regex_1, text): 
     match = match_ref[0] 
     ### ... 
    elif re_match_cond (match_ref, regex_2, text): 
     match = match_ref[0] 
     ### ... 
    elif re_match_cond (match_ref, regex_3, text): 
     match = match_ref[0] 
     ### ... 
    else: 
     ### no match 
     ### ... 

Es decir, que pase una lista a la función de emular pase por referencia.