Альтернатива `match = re.match(); if match:... `idiom?
Если вы хотите проверить, соответствует ли что-либо регулярному выражению, если это так, напечатайте первую группу, вы делаете..
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
Любые идеи? Как я уже сказал, это действительно тривиальное раздражение, почти до того, что он является кодовым гольф-клубом.
Ответы
Ответ 1
Я не думаю, что это тривиально. Я не хочу, чтобы мне приходилось обходить избыточный условный код вокруг моего кода, если я часто пишу код.
Это немного странно, но вы можете сделать это с помощью итератора:
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))
Странно то, что он использует итератор для чего-то, что не выполняет итерации - он ближе к условному, и на первый взгляд может показаться, что он даст несколько результатов для каждого соответствия.
Кажется странным, что диспетчер контекста не может полностью пропустить свою управляемую функцию; в то время как это явно не один из вариантов использования "с", это похоже на естественное расширение.
Ответ 2
Другим приятным синтаксисом будет что-то вроде этого:
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
Ответ 3
У меня есть другой способ сделать это, основываясь на решении Глена Мейнарда:
for match in [m for m in [re.match(pattern,key)] if m]:
print "It matched: %s" % match
Подобно решению Глена, это итерации либо 0 (если нет совпадений), либо 1 (если совпадают).
В результате не требуется никакого вспомогательного элемента, но в результате он менее чист.
Ответ 4
Я не думаю, что использование with
является решением в этом случае. Вам нужно было бы создать исключение в части BLOCK
(которая указана пользователем) и использовать метод __exit__
return True
для "проглатывания" исключения. Поэтому он никогда не будет выглядеть хорошо.
Я предлагаю перейти к синтаксису, подобному синтаксису Perl. Создайте собственный расширенный модуль re
(я буду называть его rex
) и пусть он задает переменные в пространстве имен модулей:
if rex.match('(\d+)g', '123g'):
print rex._1
Как вы можете видеть в комментариях ниже, этот метод не является ни облачным, ни потокобезопасным. Вы использовали бы это только в том случае, если бы полностью были уверены, что ваше приложение не станет многопоточным в будущем и что любые функции, вызванные из области, в которой вы используете это, также будут использовать тот же метод.
Ответ 5
Если вы делаете много из них в одном месте, вот альтернативный ответ:
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.
Ответ 6
Это не очень красиво, но вы можете получить прибыль от встроенной функции 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)
''
Чтобы подражать потоку группы печати if match, вы можете (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))
>>>
Ответ 7
Не идеальное решение, но позволяет вам связывать несколько вариантов соответствия для одной и той же 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"
Ответ 8
Здесь мое решение:
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')
Оба добавить и обновление возвращают Нет. Таким образом, вы должны фактически проверить результат своего выражения, используя или часть в каждом случае.
К сожалению, это работает только при условии, что код находится на верхнем уровне, т.е. не в функции.
Ответ 9
Это то, что я делаю:
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
### ...
То есть, я передаю список функции для эмулирования pass-by-reference.
Ответ 10
Начиная с 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))
...
>>>