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

Python 替代'match=re.match();如果匹配:…`成语?,python,idioms,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 } 从 …我认为这是一个有趣的想法,所以我写了一

如果要检查某项内容是否与正则表达式匹配,如果是,请打印第一组,您可以

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
}

…我认为这是一个有趣的想法,所以我写了一个简单的实现:

#!/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))
(理论上,此功能可以修补到
\u sre.sre\u Match
对象中)

如果没有匹配项,您可以使用语句的代码块跳过
的执行,这将非常好,这将简化为

with rematch("(\d+)g", "123") as m:
    print(m.group(1)) # only executed if the match occurred
…但根据我的推断,这似乎是不可能的


有什么想法吗?正如我所说,这真的是一个微不足道的烦恼,几乎到了代码高尔夫的地步。

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

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

if rex.match('(\d+)g', '123g'):
    print rex._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))
奇怪的是,它使用一个迭代器来处理一些不需要迭代的东西——它更接近于一个条件,乍一看,它可能会为每个匹配产生多个结果


上下文管理器不能使其托管功能被完全跳过,这似乎很奇怪;虽然这不是“with”的一个明确的用例,但它似乎是一个自然的扩展。

如果您在一个地方做了很多这样的事情,这里有一个替代答案:

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相同的线程安全性编译regex一次,为整个函数创建一个可重用的Matcher对象,然后可以非常简洁地使用它。这还有一个好处,就是您可以用一种显而易见的方式反转它——要用迭代器实现这一点,您需要传递一个标志来告诉它反转其结果


但是,如果您只对每个函数进行一次匹配,则没有多大帮助;您不希望将Matcher对象保留在更广泛的上下文中;这将导致与Blixt的解决方案相同的问题。

这看起来并不漂亮,但您可以像这样使用
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)
''
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
要模拟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))
>>> 

这不是完美的解决方案,但确实允许您为同一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"

另一个很好的语法如下:

>>> getattr(re.match("(\d+)g", "123g"), 'group', lambda n:'')(1)
'123'
>>> getattr(re.match("(\d+)g", "X23g"), 'group', lambda n:'')(1)
''
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

基于格伦·梅纳德的解决方案,我有另一种方法:

for match in [m for m in [re.match(pattern,key)] if m]:
    print "It matched: %s" % match
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')
与Glen的解决方案类似,该方法计算0次(如果不匹配)或1次(如果匹配)

不需要接头,但因此不太整洁。

以下是我的解决方案:

for match in [m for m in [re.match(pattern,key)] if m]:
    print "It matched: %s" % match
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')
追加更新返回。因此,您必须在每种情况下使用部分来检查表达式的结果

不幸的是,这只适用于代码位于顶层的情况,即不在函数中。

我就是这样做的:

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

也就是说,我将一个列表传递给函数以模拟按引用传递。

启动
python3.8
,并引入(
:=
操作符),我们现在可以捕获条件值
re.match(r'(\d+)g',123g'))
在变量
中匹配
,以检查其是否为
,然后在条件主体内重复使用:

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

小心这个。如果条件中的代码重用“rex”对象,它就会崩溃,因此为了安全起见,在使用它时,您需要创建一个副本——这主要是破坏了它的好处。它也不是螺纹安全的,除非你跳过一些TLS箍;re对象上的match()应该是完全线程安全的。我认为这些问题远远超过了这样做的好处——保存一行代码需要记住很多。在某些情况下,我相信好处大于问题。对于简单的单线程程序,我相信这种方法是可以的。然而,“match=re.match(…);if match:…”是python的惯用用法。我自己会继续这样做的。仍然+1到@blix的答案,这是对原始问题的优雅、类似perl的答案。给出的示例是perl的魔术变量,它们具有相同的限制。我看不到任何其他“好看”的方法可以使代码比提取对象并检查它是否
None
更短。优雅和类似Perl的描述正好相反。老实说,我认为这些解决方案都不应该被研究。要保存一行代码,只需考虑一个简单的解决方案。我甚至不认为让我的代码线程安全是值得的,因为如果你想要线程安全的代码,你应该使用对象,正如已经证明的,在一个语句中分配和检查对象的唯一方法是Glenn使用生成器发布的解决方法,这不是很直观