Альтернатива `match = re.match (); если совпадение: ... `идиома? - PullRequest
33 голосов
/ 20 июля 2009

Если вы хотите проверить, соответствует ли что-либо регулярному выражению, если это так, выведите первую группу, то вы сделаете ..

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

Это совершенно педантично, но промежуточная переменная match немного раздражает.

Такие языки, как Perl, делают это путем создания новых $1 .. $9 переменных для групп соответствия, например ..

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

Из этого комментария reddit ,

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

.., которая мне показалась интересной, поэтому я написал простую реализацию:

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

(Эта функция теоретически может быть исправлена ​​в объекте _sre.SRE_Match)

Было бы неплохо, если бы вы могли пропустить выполнение блока кода оператора with, если совпадений не было, что упростило бы это до ..

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

.. но это кажется невозможным, исходя из того, что я могу вывести из PEP 343

Есть идеи? Как я уже сказал, это действительно тривиальное раздражение, почти на грани того, чтобы быть code-golf ..

Ответы [ 10 ]

12 голосов
/ 20 июля 2009

Я не думаю, что это тривиально. Я не хочу разбрасывать избыточные условные выражения вокруг моего кода, если я пишу такой код часто.

Это немного странно, но вы можете сделать это с помощью итератора:

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

Странно то, что он использует итератор для чего-то, что не повторяется - оно ближе к условному, и на первый взгляд может показаться, что оно даст несколько результатов для каждого совпадения.

Кажется странным, что менеджер контекста не может полностью пропустить свою управляемую функцию; хотя это явно не один из вариантов использования «with», это выглядит как естественное расширение.

4 голосов
/ 05 декабря 2012

Другой хороший синтаксис будет выглядеть примерно так:

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
2 голосов
/ 27 апреля 2019

Начиная с Python 3.8 и введением выражений присваивания (PEP 572) (оператор :=), теперь мы можем захватить значение условия re.match(r'(\d+)g', '123g') в переменной match, чтобы оба проверьте, не является ли он None, а затем повторно используйте его в теле условия:

>>> if match := re.match(r'(\d+)g', '123g'):
...   print(match.group(1))
... 
123
>>> if match := re.match(r'(\d+)g', 'dddf'):
...   print(match.group(1))
...
>>>
1 голос
/ 12 ноября 2013

У меня есть другой способ сделать это, основываясь на решении Глена Мейнарда:

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

Аналогично решению Глена, оно повторяется либо 0 (если нет совпадений), либо 1 (если совпадение).

Подставка не нужна, но в результате менее аккуратно.

0 голосов
/ 09 октября 2014

Вот что я делаю:

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

То есть я передаю список функции для эмуляции передачи по ссылке.

0 голосов
/ 03 мая 2014

Вот мое решение:

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

Вы можете использовать столько elif предложений, сколько необходимо.

Еще лучше:

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

Обе , добавление и , обновление , возврат Нет . Таким образом, вы должны на самом деле проверить результат вашего выражения, используя в каждом случае часть или .

К сожалению, это работает только до тех пор, пока код находится на верхнем уровне, т.е. не в функции.

0 голосов
/ 15 декабря 2011

Не идеальное решение, но позволяет объединить несколько вариантов совпадения для одной и той же строки:

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 голосов
/ 06 октября 2011

Это не очень красиво выглядит, но вы можете воспользоваться встроенной функцией getattr(object, name[, default]), используя ее следующим образом:

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

Чтобы имитировать поток , если соответствует поток печати группы , вы можете (ab) использовать оператор for следующим образом:

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

Конечно, вы можете определить небольшую функцию для грязной работы:

>>> 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 голосов
/ 20 июля 2009

Если вы делаете много из них в одном месте, вот альтернативный ответ:

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"

Вы можете скомпилировать регулярное выражение один раз с той же безопасностью потока, что и re, создать один повторно используемый объект Matcher для всей функции, а затем использовать его очень кратко. Это также имеет то преимущество, что вы можете полностью изменить его очевидным образом - чтобы сделать это с итератором, вам нужно будет передать флаг, чтобы сказать ему инвертировать его результат.

Это не сильно поможет, если вы выполняете только одно совпадение для каждой функции; Вы не хотите хранить объекты Matcher в более широком контексте; это вызовет те же проблемы, что и решение Blixt.

0 голосов
/ 20 июля 2009

Я не думаю, что использование with является решением в этом случае. Вы должны были бы вызвать исключение в части BLOCK (которая указана пользователем) и иметь метод __exit__, возвращающий True, чтобы "проглотить" исключение. Так что это никогда не будет хорошо выглядеть.

Я бы предложил использовать синтаксис, аналогичный синтаксису Perl. Создайте свой собственный расширенный модуль re (я назову его rex), и он установит переменные в пространстве имен модуля:

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

Как вы можете видеть в комментариях ниже, этот метод не является ни ориентированным ни на поток, ни на поток. Вы могли бы использовать это, только если были полностью уверены, что ваше приложение не станет многопоточным в будущем и что любые функции, вызываемые из области, в которой вы используете это, будут использовать тот же метод .

...