在Python 3中,该re
模块可以与一起使用memoryview
:
~$ python3
Python 3.2.3 (default, Feb 20 2013, 14:44:27)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = b"abc"
>>> import re
>>> re.search(b"b", memoryview(x))
<_sre.SRE_Match object at 0x7f14b5fb8988>
但是,在Python 2中,情况似乎并非如此:
~$ python
Python 2.7.3 (default, Mar 13 2014, 11:03:55)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = "abc"
>>> import re
>>> re.search(b"b", memoryview(x))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/re.py", line 142, in search
return _compile(pattern, flags).search(string)
TypeError: expected string or buffer
我可以将字符串转换为a buffer
,但是在缓冲区文档中,它没有确切地提到buffer
与a相比是如何工作的memoryview
。
进行经验比较表明,buffer
在Python 2中使用对象不能提供memoryview
在Python 3中使用对象的性能优势:
playground$ cat speed-test.py
import timeit
import sys
print(timeit.timeit("regex.search(mv[10:])", setup='''
import re
regex = re.compile(b"ABC")
PYTHON_3 = sys.version_info >= (3, )
if PYTHON_3:
mv = memoryview(b"Can you count to three or sing 'ABC?'" * 1024)
else:
mv = buffer(b"Can you count to three or sing 'ABC?'" * 1024)
'''))
playground$ python2.7 speed-test.py
2.33041596413
playground$ python2.7 speed-test.py
2.3322429657
playground$ python3.2 speed-test.py
0.381270170211792
playground$ python3.2 speed-test.py
0.3775448799133301
playground$
如果将regex.search
参数从更改mv[10:]
为mv
,则Python 2的性能与Python 3的性能大致相同,但是在我编写的代码中,有很多重复的字符串切片。
有没有一种方法可以在Python 2中规避此问题,同时仍然具有零复制性能的好处memoryview
?
据我了解Python 2中的缓冲区对象的方式,您应该在不切片的情况下使用它:
>>> s = b"Can you count to three or sing 'ABC?'"
>>> str(buffer(s, 10))
"unt to three or sing 'ABC?'"
因此,无需切片结果缓冲区,而是直接使用buffer函数执行切片,从而可以快速访问您感兴趣的子字符串:
import timeit
import sys
import re
r = re.compile(b'ABC')
s = b"Can you count to three or sing 'ABC?'" * 1024
PYTHON_3 = sys.version_info >= (3, )
if len(sys.argv) > 1: # standard slicing
print(timeit.timeit("r.search(s[10:])", setup='from __main__ import r, s'))
elif PYTHON_3: # memoryview in Python 3
print(timeit.timeit("r.search(s[10:])", setup='from __main__ import r, s; s = memoryview(s)'))
else: # buffer in Python 2
print(timeit.timeit("r.search(buffer(s, 10))", setup='from __main__ import r, s'))
我在Python 2和3中获得了非常相似的结果,这表明buffer
与re
模块一起使用类似的效果与较新的模块memoryview
(后者似乎是延迟计算的缓冲区)相似:
$ python2 .\speed-test.py
0.681979371561
$ python3 .\speed-test.py
0.5693422508853488
并与标准字符串切片进行比较:
$ python2 .\speed-test.py standard-slicing
7.92006735956
$ python3 .\speed-test.py standard-slicing
7.817641705304309
如果要支持切片访问(以便可以在任何地方使用相同的语法),则可以轻松创建一个在切片时动态创建新缓冲区的类型:
class slicingbuffer:
def __init__ (self, source):
self.source = source
def __getitem__ (self, index):
if not isinstance(index, slice):
return buffer(self.source, index, 1)
elif index.stop is None:
return buffer(self.source, index.start)
else:
end = max(index.stop - index.start, 0)
return buffer(self.source, index.start, end)
如果仅将其与re
模块一起使用,它可能会直接替代memoryview
。但是,我的测试表明,这已经给您带来了巨大的开销。因此,您可能想做相反的事情,并将Python 3的memoryview对象包装在一个包装器中,该包装器为您提供与以下相同的接口buffer
:
def memoryviewbuffer (source, start, end = -1):
return source[start:end]
PYTHON_3 = sys.version_info >= (3, )
if PYTHON_3:
b = memoryviewbuffer
s = memoryview(s)
else:
b = buffer
print(timeit.timeit("r.search(b(s, 10))", setup='from __main__ import r, s, b'))
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句