替代`match = re.match();如果匹配:...`成语?

时间:2009-07-20 08:40:43

标签: python idioms

如果你想检查某些东西是否与正则表达式相匹配,如果是,打印第一组,就可以了..

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
}

来自this reddit comment

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

推断出的内容,这似乎是不可能的

有什么想法吗?正如我所说,这实际上是微不足道的烦恼,几乎到了代码高尔夫......

10 个答案:

答案 0 :(得分:12)

我不认为这是微不足道的。如果我经常编写这样的代码,我不想在我的代码周围添加冗余条件。

这有点奇怪,但您可以使用迭代器执行此操作:

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”的用例之一,但它似乎是一种自然延伸。

答案 1 :(得分:4)

另一个很好的语法是这样的:

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 :(得分:2)

Python 3.8开始并引入assignment expressions (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))
...
>>>

答案 3 :(得分:1)

基于Glen Maynard的解决方案,我有另一种方法:

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

与Glen的解决方案类似,它会改为0(如果不匹配)或1(如果匹配)次。

不需要子,但结果不那么整洁。

答案 4 :(得分:0)

我不认为在这种情况下使用with是解决方案。您必须在BLOCK部分(由用户指定)中引发异常,并让__exit__方法返回True以“吞下”异常。所以它永远不会好看。

我建议使用类似于Perl语法的语法。制作你自己的扩展re模块(我称之为rex)并让它在模块命名空间中设置变量:

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

正如您在下面的评论中所看到的,此方法既不是范围也不是线程安全的。如果你完全确定你的应用程序将来不会变成多线程,那么你只会使用它,并且从你使用它的范围调用的任何函数使用相同的函数方法

答案 5 :(得分:0)

如果你在一个地方做了很多这样的话,这里有另一个答案:

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 :(得分:0)

这看起来并不漂亮,但您可以使用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 print group 流程,您可以(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 :(得分:0)

不是完美的解决方案,但允许您为同一个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 :(得分:0)

这是我的解决方案:

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 :(得分:0)

这就是我的所作所为:

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。