Alternative to the `match = re.match(); if match: ...` idiom?

I don't think it's trivial. I don't want to have to sprinkle a redundant conditional around my code if I'm writing code like that often.

This is slightly odd, but you can do this with an iterator:

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

The odd thing is that it's using an iterator for something that isn't iterating--it's closer to a conditional, and at first glance it might look like it's going to yield multiple results for each match.

It does seem odd that a context manager can't cause its managed function to be skipped entirely; while that's not explicitly one of the use cases of "with", it seems like a natural extension.


Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), we can now capture the condition value re.match(r'(\d+)g', '123g') in a variable match in order to both check if it's not None and then re-use it within the body of the condition:

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

I have another way of doing this, based on Glen Maynard's solution:

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

Similar to Glen's solution, this itterates either 0 (if no match) or 1 (if a match) times.

No sub needed, but less tidy as a result.


Another nice syntax would be something like this:

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

Tags:

Python

Idioms