Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/300.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:在哪里编译RE?_Python_Performance - Fatal编程技术网

Python:在哪里编译RE?

Python:在哪里编译RE?,python,performance,Python,Performance,我有一个函数,它通过使用正则表达式查找信息来迭代文本文件以提取某些信息。但是,程序会遍历多个文件,因此会多次调用此函数 目前,我已将正则表达式编译为函数的第一步。但我开始怀疑,从编程的角度来看,这是否是一个好的设计,因为函数会被多次调用 解释器是否足够聪明,可以看到它们不会改变,并在运行之间缓存它们?或者,我考虑将它们编译为全局变量,以便它始终只能编译一次,但这会将正则表达式与其使用位置分开,从而使其更难读取。我看到的另一个选项是将函数创建为闭包,并在创建时传递正则表达式值,但这似乎不必要地复杂

我有一个函数,它通过使用正则表达式查找信息来迭代文本文件以提取某些信息。但是,程序会遍历多个文件,因此会多次调用此函数

目前,我已将正则表达式编译为函数的第一步。但我开始怀疑,从编程的角度来看,这是否是一个好的设计,因为函数会被多次调用

解释器是否足够聪明,可以看到它们不会改变,并在运行之间缓存它们?或者,我考虑将它们编译为全局变量,以便它始终只能编译一次,但这会将正则表达式与其使用位置分开,从而使其更难读取。我看到的另一个选项是将函数创建为闭包,并在创建时传递正则表达式值,但这似乎不必要地复杂

简言之,编译RE(或任何其他计算过一次的值)的最有效方法是什么?它仍然是可读的和pythonic的?


谢谢。

Python的正则表达式模块确实缓存了最近使用的正则表达式的编译版本,因此您可以删除显式编译,而不会出现问题。

您可以编写一个简单的
RegexCache
类。类似于(未经测试):

然后,而不是:

re.search(complex_pattern, whatver)
你可以:

re.search(recache.get(complex_pattern), whatver)

这将使模式与使用它的位置保持一致,同时也确保您多次使用的模式将被编译。您还必须确保缓存不会变得太大。

必须注意:在进入这个兔子洞之前,始终确保这是您的瓶颈。如果只运行正则表达式几次,并且正则表达式不是特别复杂,那么节省的时间可能很小

如果计划多次运行正则表达式操作,最好使用
re.compile
。然而,如果没有更多细节,最多可以说的是:您应该显式地进行测试。使用timeit或其他模块为流程计时

至于缓存已编译的正则表达式,无论它是否在后台缓存,如果直接使用re函数而不是先编译,都将付出代价。要查看这一点,您应该使用
dis
查看它的功能:

>>> def f():
...     x="foo bar baz"
...     return re.match("foo", x)
...
>>> dis.dis(f)
  2           0 LOAD_CONST               1 ('foo bar baz')
              3 STORE_FAST               0 (x)

  3           6 LOAD_GLOBAL              0 (re)
              9 LOAD_ATTR                1 (match)
             12 LOAD_CONST               2 ('foo') ** always have to pass the regex
             15 LOAD_FAST                0 (x)
             18 CALL_FUNCTION            2
             21 RETURN_VALUE
>>> n=re.compile("foo")
>>> def g():
...     x="foo bar baz"
...     return n.match("foo")
...
>>> dis.dis(g)
  2           0 LOAD_CONST               1 ('foo bar baz')
              3 STORE_FAST               0 (x)

  3           6 LOAD_GLOBAL              0 (n)
              9 LOAD_ATTR                1 (match)
             12 LOAD_FAST                0 (x)
             15 CALL_FUNCTION            1
             18 RETURN_VALUE

因此,即使它在后台缓存正则表达式,计划也必须将正则表达式传递给
re.match
(这是不可避免的)。编译后的版本避免了这一步。

将re编译并作为模块级对象使用没有什么错。并不是说它始终是当前的最佳实践,而是指在stdlib的各个模块中使用的


对我来说,使用模块级编译的re对象比3.3的缓存更可取,因为缓存依赖于您可能无法控制(或在未来版本中可能会更改)的实现细节。在模块范围内定义它们可以让代码的读者清楚地看到,它们只编译了一次,使用了N次。

您有相关的源代码吗?@AmirRachum:有。默认情况下,Python 3.3中最多会缓存512个最后的模式,请参阅。谢谢。听起来,除非使用了大量的模式,否则信任缓存是一条可行之路。我相信这是一种过早优化的情况。在你的情况下,做你觉得最容易理解的事情。如果您有性能问题,请运行探查器,看看问题出在哪里。@AmirRachum在这种情况下,您可能是对的。但是,这不是我第一次或最后一次有这样的设计,所以很高兴尽早将其正确,至少在不损害可读性的情况下;你只是增加了更多的开销。@Amber:也许,这取决于你使用了多少正则表达式。还有一种说法,显式比隐式好。此外,如果超过python缓存大小,它只会立即清除所有内容,因此如果您说
cache\u size+1
regex是一行使用的,那么它不会缓存任何内容。由于python会自动缓存最近的regex,因此,如果要大量运行,您只需要显式编译(想想数百个)重复使用不同的正则表达式,每次多次。
>>> def f():
...     x="foo bar baz"
...     return re.match("foo", x)
...
>>> dis.dis(f)
  2           0 LOAD_CONST               1 ('foo bar baz')
              3 STORE_FAST               0 (x)

  3           6 LOAD_GLOBAL              0 (re)
              9 LOAD_ATTR                1 (match)
             12 LOAD_CONST               2 ('foo') ** always have to pass the regex
             15 LOAD_FAST                0 (x)
             18 CALL_FUNCTION            2
             21 RETURN_VALUE
>>> n=re.compile("foo")
>>> def g():
...     x="foo bar baz"
...     return n.match("foo")
...
>>> dis.dis(g)
  2           0 LOAD_CONST               1 ('foo bar baz')
              3 STORE_FAST               0 (x)

  3           6 LOAD_GLOBAL              0 (n)
              9 LOAD_ATTR                1 (match)
             12 LOAD_FAST                0 (x)
             15 CALL_FUNCTION            1
             18 RETURN_VALUE