Python,迭代正则表达式但在第一次匹配时停止的最快方法

puf*_*uff 15 python performance python-3.x

我有一个函数,如果一个字符串匹配列表中的至少一个正则表达式,则返回True,否则返回False.经常调用该函数,性能是一个问题.

当通过cProfile运行时,该函数花费大约65%的时间进行匹配,35%的时间遍历列表.

我认为有一种方法可以使用map()或其他东西,但我想不出一种让它在找到匹配后停止迭代的方法.

有没有办法让这个功能更快,同时还能在找到第一个匹配后返回它?

def matches_pattern(str, patterns):
    for pattern in patterns:
        if pattern.match(str):
            return True
    return False
Run Code Online (Sandbox Code Playgroud)

Nik*_* B. 23

首先想到的是使用生成器表达式将循环推送到C端:

def matches_pattern(s, patterns):
    return any(p.match(s) for p in patterns)
Run Code Online (Sandbox Code Playgroud)

可能你甚至不需要单独的功能.

您应该尝试的另一件事是使用|交替运算符构建单个复合正则表达式,以便引擎有机会为您优化它.如果有必要,您还可以从字符串模式列表中动态创建正则表达式:

def matches_pattern(s, patterns):
    return re.match('|'.join('(?:%s)' % p for p in patterns), s)
Run Code Online (Sandbox Code Playgroud)

当然,你需要以字符串形式使用你的正则表达式才能工作.只需分析这两个并检查哪一个更快:)

您可能还想查看在Python中调试正则表达式一般提示.这也有助于找到优化的机会.

更新:我好奇并写了一个小基准:

import timeit

setup = """
import re
patterns = [".*abc", "123.*", "ab.*", "foo.*bar", "11010.*", "1[^o]*"]*10
strings = ["asdabc", "123awd2", "abasdae23", "fooasdabar", "111", "11010100101", "xxxx", "eeeeee", "dddddddddddddd", "ffffff"]*10
compiled_patterns = list(map(re.compile, patterns))

def matches_pattern(str, patterns):
    for pattern in patterns:
        if pattern.match(str):
            return True
    return False

def test0():
    for s in strings:
        matches_pattern(s, compiled_patterns)

def test1():
    for s in strings:
        any(p.match(s) for p in compiled_patterns)

def test2():
    for s in strings:
        re.match('|'.join('(?:%s)' % p for p in patterns), s)

def test3():
    r = re.compile('|'.join('(?:%s)' % p for p in patterns))
    for s in strings:
        r.match(s)
"""

import sys
print(timeit.timeit("test0()", setup=setup, number=1000))
print(timeit.timeit("test1()", setup=setup, number=1000))
print(timeit.timeit("test2()", setup=setup, number=1000))
print(timeit.timeit("test3()", setup=setup, number=1000))
Run Code Online (Sandbox Code Playgroud)

我机器上的输出:

1.4120500087738037
1.662621021270752
4.729579925537109
0.1489570140838623
Run Code Online (Sandbox Code Playgroud)

因此,any似乎没有比原始方法更快.动态构建正则表达式也不是很快.但是,如果您可以设法预先构建一个正则表达式并多次使用它,这可能会带来更好的性能.您还可以调整此基准测试以测试其他一些选项:)


Ned*_*der 8

最快速的方法是将所有正则表达式组合在一起"|",然后进行一次正则表达式匹配调用.此外,您需要编译一次,以确保您避免重复的正则表达式编译.

例如:

def matches_pattern(s, pats):
    pat = "|".join("(%s)" % p for p in pats)
    return bool(re.match(pat, s))
Run Code Online (Sandbox Code Playgroud)

这是pats作为字符串,而不是编译模式.如果你真的只编译了正则表达式,那么:

def matches_pattern(s, pats):
    pat = "|".join("(%s)" % p.pattern for p in pats)
    return bool(re.match(pat, s))
Run Code Online (Sandbox Code Playgroud)