我在Python中发现的最大烦恼之一是re模块无法在匹配对象中明确地保存其状态而无法保存其状态.通常,需要解析行,如果它们符合某个正则表达式,则从相同的正则表达式中取出它们的值.我想写这样的代码:
if re.match('foo (\w+) bar (\d+)', line):
# do stuff with .group(1) and .group(2)
elif re.match('baz whoo_(\d+)', line):
# do stuff with .group(1)
# etc.
Run Code Online (Sandbox Code Playgroud)
但遗憾的是,无法找到前一次调用的匹配对象re.match,所以这样写的如下:
m = re.match('foo (\w+) bar (\d+)', line)
if m:
# do stuff with m.group(1) and m.group(2)
else:
m = re.match('baz whoo_(\d+)', line)
if m:
# do stuff with m.group(1)
Run Code Online (Sandbox Code Playgroud)
随着elifs 的列表变长,这样不太方便并且变得非常笨拙.
一个hackish解决方案是将re.match和re.search包装在我自己的对象中,以保持状态.有没有人用过这个?您是否了解半标准实现(在大型框架或其他内容中)?
您还可以推荐哪些其他解决方法?或许,我只是在滥用模块,能够以更清洁的方式满足我的需求吗?
提前致谢
尝试一些想法...
看来您理想地想要一个带有副作用的表达式。如果 Python 允许这样做:
if m = re.match('foo (\w+) bar (\d+)', line):
# do stuff with m.group(1) and m.group(2)
elif m = re.match('baz whoo_(\d+)', line):
# do stuff with m.group(1)
elif ...
Run Code Online (Sandbox Code Playgroud)
...那么您就可以清楚、干净地表达您的意图。但事实并非如此。如果嵌套函数中允许副作用,您可以:
m = None
def assign_m(x):
m = x
return x
if assign_m(re.match('foo (\w+) bar (\d+)', line)):
# do stuff with m.group(1) and m.group(2)
elif assign_m(re.match('baz whoo_(\d+)', line)):
# do stuff with m.group(1)
elif ...
Run Code Online (Sandbox Code Playgroud)
现在,这不仅变得丑陋,而且仍然不是有效的 Python 代码——不允许嵌套函数“assign_m”修改m外部作用域中的变量。我能想到的最好的办法真的很难看,使用允许副作用的嵌套类:
# per Brian's suggestion, a wrapper that is stateful
class m_(object):
def match(self, *args):
self.inner_ = re.match(*args)
return self.inner_
def group(self, *args):
return self.inner_.group(*args)
m = m_()
# now 'm' is a stateful regex
if m.match('foo (\w+) bar (\d+)', line):
# do stuff with m.group(1) and m.group(2)
elif m.match('baz whoo_(\d+)', line):
# do stuff with m.group(1)
elif ...
Run Code Online (Sandbox Code Playgroud)
但这显然是多余的。
您可能会考虑使用内部函数来允许局部作用域退出,这允许您删除嵌套else:
def find_the_right_match():
# now 'm' is a stateful regex
m = re.match('foo (\w+) bar (\d+)', line)
if m:
# do stuff with m.group(1) and m.group(2)
return # <== exit nested function only
m = re.match('baz whoo_(\d+)', line)
if m:
# do stuff with m.group(1)
return
find_the_right_match()
Run Code Online (Sandbox Code Playgroud)
这可以让你将nesting=(2*N-1)展平为nesting=1,但你可能只是转移了副作用问题,并且嵌套函数很可能会让大多数Python程序员感到困惑。
最后,有一些无副作用的方法来处理这个问题:
def cond_with(*phrases):
"""for each 2-tuple, invokes first item. the first pair where
the first item returns logical true, result is passed to second
function in pair. Like an if-elif-elif.. chain"""
for (cond_lambda, then_lambda) in phrases:
c = cond_lambda()
if c:
return then_lambda(c)
return None
cond_with(
((lambda: re.match('foo (\w+) bar (\d+)', line)),
(lambda m:
... # do stuff with m.group(1) and m.group(2)
)),
((lambda: re.match('baz whoo_(\d+)', line)),
(lambda m:
... # do stuff with m.group(1)
)),
...)
Run Code Online (Sandbox Code Playgroud)
现在代码看起来几乎不像 Python,更不用说 Python 程序员可以理解了(是 Lisp 吗?)。
我认为这个故事的寓意是 Python 没有针对这种习惯进行优化。您确实需要稍微冗长一些,并忍受其他条件的大量嵌套因素。