2009-07-20 7 views
29

Wenn Sie überprüfen wollen, ob etwas einen regulären Ausdruck übereinstimmt, wenn ja, die erste Gruppe drucken, was Sie tun ..Alternative zum `match = re.match(); wenn Übereinstimmung: ... `Idiom?

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

Das ist völlig pedantisch, aber die Zwischen match Variable ist ein bisschen ärgerlich ..

Sprachen wie Perl tun dies, indem neue $1 Schaffung .. $9 Variablen für Match-Gruppen, wie ..

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

von this reddit comment,

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

..welche ich dachte, war eine interessante Idee, so schrieb ich eine einfache Implementierung davon: Es

#!/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)) 

(Diese Funktionalität theoretisch in das _sre.SRE_Match Objekt gepatcht werden könnte)

wäre schön, wenn Sie die Ausführung des Codeblocks der with Anweisung überspringen könnten, wenn es keine Übereinstimmung gäbe, die das vereinfachen würde.

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

..aber dies unmöglich scheint anhand dessen, was ich von PEP 343

Irgendwelche Ideen ableiten kann? Wie gesagt, das ist wirklich trivialer Ärger, fast bis zum Code-Golf.

+1

„fast bis zum Punkt der Code-Golf ist“ nicht einverstanden ist. Dies ist absolut code golf. Ich kann nicht sehen, was "nervig" ist, wenn eine Variable zur Darstellung der Zustandsänderung gesetzt wird. –

+0

Ich habe sage ich pedantisch wurde .. – dbr

+2

ähnliche Diskussion bereits auf SO: http://stackoverflow.com/questions/447086/pythons-re-module-saving-state –

Antwort

12

Ich glaube nicht, dass es trivial ist. Ich möchte nicht eine redundante Bedingung um meinen Code streuen müssen, wenn ich Code so häufig schreibe.

Dies ist etwas seltsam, aber man kann mit einem Iterator dies tun:

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)) 

Das Seltsame ist, dass es einen Iterator für etwas ist, verwenden, die nicht iteriert - es ist näher an einem bedingten, und Auf den ersten Blick sieht es so aus, als würde es für jedes Spiel mehrere Ergebnisse liefern.

Es scheint seltsam, dass ein Kontextmanager nicht dazu führen kann, dass seine verwaltete Funktion vollständig übersprungen wird; obwohl das nicht explizit einer der Anwendungsfälle von "mit" ist, scheint es eine natürliche Erweiterung zu sein.

+0

Ja, es würde funktionieren, wenn der '__enter__' Code innerhalb des' try' Teils ausgeführt wurde, dass die '__exit__' Code Kontrolle über die Ausnahmen (weil der' __enter__' Code dann eine spezielle Art von Ausnahme auslösen könnte gibt, die durch Verschlucken die 'with'-Anweisung, die effektiv verhindert, dass irgendein Code in ihr ausgeführt wird.) Im Moment sehe ich jedoch keinen Weg, das zu umgehen. – Blixt

+3

Es wäre schön, wenn Python Zuordnung erlaubt in Ausdrücken, wie C: "Wenn x = y():", "wenn nicht (x = y():";. Sie dies ohne weiteres handhaben würden –

+0

+1: Dieses ist die einzige praktikable Lösung zum Speichern einer Codezeile.Während die Verwendung eines Generators nicht sehr intuitiv ist, erfüllt er die Aufgabe und ist sowohl für den Umfang als auch für den Thread sicher. – Blixt

0

Ich glaube nicht, with ist die Lösung in diesem Fall. Sie müssten eine Ausnahme im BLOCK-Teil (der vom Benutzer angegeben wurde) auslösen und die __exit__-Methode True zurückgeben, um die Ausnahme zu "verschlucken". So würde es nie gut aussehen.

Ich würde vorschlagen, für eine Syntax ähnlich der Perl-Syntax gehen. Machen Sie Ihre eigenen erweitert re Modul (ich nenne es rex) und haben es die Variablen in seiner Modul-Namensraum:

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

Wie Sie in den Kommentaren unten sehen können, ist dieses Verfahren weder Scope- noch thread-safe . Sie würden dies nur verwenden, wenn Sie absolut sicher wären, dass Ihre Anwendung in Zukunft nicht multi-threaded wird und dass alle Funktionen, die aus dem Bereich aufgerufen werden, in dem Sie diese verwenden, auch die gleiche Methode verwenden.

+1

diese Vorsicht. Es wird zerbrechen, wenn der Code innerhalb des Conditionals das "rex" -Objekt wiederverwendet, um sicher zu sein, dass Sie in diesem Fall eine Kopie erstellen müssen, wenn Sie es verwenden - und das macht den Vorteil größtenteils rückgängig. Es ist auch nicht threadsafe, es sei denn, Sie springen einige TLS-Reifen; match() auf ein re-Objekt (sollte sein) vollständig threadsafe. Ich denke, dass diese Probleme bei weitem die Vorteile überwiegen, wenn man so vorgeht - es ist eine Menge zu beachten, um eine Codezeile zu speichern. –

+0

und Thread-Locals Multithreading zu behandeln? –

+0

In einigen Fällen glaube ich, dass die Vorteile die Probleme überwiegen. Für einfache Single-Thread-Programme glaube ich, dass dieser Ansatz in Ordnung ist. "Match = re.match (...); if match: ..." ist idiomatisch python. Ich werde es weiterhin so machen. Noch +1 zu @ Blixt's Antwort, für eine elegante, perlartige Antwort auf die ursprüngliche Frage. – codeape

0

Wenn Sie eine Menge davon an einem Ort tun, hier ist eine Alternative Antwort:

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" 

Sie die Regex einmal mit der gleichen Thread-Sicherheit wie Re, erstellen Sie ein einzelnes wiederverwendbares Matcher Objekt kompilieren die ganze Funktion, und dann können Sie es sehr prägnant verwenden. Dies hat auch den Vorteil, dass Sie es auf die offensichtliche Weise umkehren können - um dies mit einem Iterator zu tun, müssten Sie ein Flag übergeben, um es zu veranlassen, sein Ergebnis zu invertieren.

Es ist nicht viel hilfreich, wenn Sie nur eine einzige Übereinstimmung pro Funktion machen, obwohl; Sie möchten Matcher-Objekte nicht in einem breiteren Kontext halten; Es würde die gleichen Probleme wie Blixt Lösung verursachen. Diese

0

ist nicht wirklich hübsch aussehende, aber Sie können von der getattr(object, name[, default]) eingebaute Funktion verwenden es wie folgt profitieren:

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

zu imitieren die wenn Match Druckgruppe Flow, können Sie (ab verwenden) auf diese Weise die for Aussage:

>>> 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)) 
>>> 

natürlich können Sie eine kleine Funktion definieren können, die schmutzige Arbeit zu tun:

>>> 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

nicht die perfekte Lösung, aber Dich Kette mehr Spiel-Optionen für die gleiche str erlaubt:

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 
4

Eine weitere nette Syntax so etwas wie dies würde Art und Weise, dies zu tun, basierend auf Glen Maynard Lösung:

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

ähnlich wie Glen-Lösung, diese itte Raten entweder 0 (wenn keine Übereinstimmung) oder 1 (wenn eine Übereinstimmung) mal.

Kein Sub benötigt, aber weniger sauber als Ergebnis.

1

Ich habe eine andere:

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" 
0

Hier ist meine Lösung:

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') 

Sie so viele elif Klauseln je nach Bedarf verwenden können.

Noch besser:

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') 

Beide anhängen und Update Rückkehr Keine. Sie müssen also das Ergebnis Ihres Ausdrucks überprüfen, indem Sie in jedem Fall den oder Teil verwenden.

Leider funktioniert das nur so lange, wie der Code oberste Ebene befindet, d. H. Nicht in einer Funktion.Diese

0

ist, was ich tue:

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 
     ### ... 

Das heißt, gebe ich eine Liste an die Funktion Pass-by-reference zu emulieren.

Verwandte Themen