简化语句 '.'.join(string.split('.')[0:3] )

Posted

技术标签:

【中文标题】简化语句 \'.\'.join(string.split(\'.\')[0:3] )【英文标题】:Simplify statement '.'.join( string.split('.')[0:3] )简化语句 '.'.join(string.split('.')[0:3] ) 【发布时间】:2015-01-30 14:08:48 【问题描述】:

我习惯用C/C++写代码,看到下面的数组操作,感觉有点浪费CPU:

version = '1.2.3.4.5-RC4'                 # the end can vary a lot
api = '.'.join( version.split('.')[0:3] ) # extract '1.2.3'

所以我想知道:

这一行是否会被执行(解释)为创建一个临时数组(内存分配),然后连接前三个单元格(再次分配内存)? 还是 python 解释器足够聪明?(我也很好奇 Pythran、Parakeet、Numba、Cython 和其他 python 解释器/编译器在这种情况下所做的优化.. .)

有没有什么技巧可以编写更高效的 CPU 替换行并且仍然可以理解/优雅?(您可以提供具体的 Python2 和/或 Python3 技巧和提示)

【问题讨论】:

这只是执行一次,不是吗?为什么要担心这个的性能呢?不,Python 不会对此进行优化;它会创建一个列表,然后创建另一个字符串。 Your code looks fine, get used to also feel fine 【参考方案1】:

为此,我不知道 CPU 使用率,但这不就是我们以某种方式使用高级语言的原因吗?

另一种解决方案是使用正则表达式,使用编译模式应该允许后台优化:

import re
version = '1.2.3.4.5-RC4'
pat = re.compile('^(\d+\.\d+\.\d+)')
res = re.match(version)
if res:
  print res.group(1)

编辑:按照@jonrsharpe 的建议,我也运行了timeit 基准测试。这是我的结果:

def extract_vers(str):
   res = pat.match(str)
   if res:
     return res.group(1)
   else:
     return False

>>> timeit.timeit("api1(s)", setup="from __main__ import extract_vers,api1,api2; s='1.2.3.4.5-RC4'")
1.9013631343841553
>>> timeit.timeit("api2(s)", setup="from __main__ import extract_vers,api1,api2; s='1.2.3.4.5-RC4'")
1.3482811450958252
>>> timeit.timeit("extract_vers(s)", setup="from __main__ import extract_vers,api1,api2; s='1.2.3.4.5-RC4'")
1.174590826034546

编辑:但无论如何,Python 中存在一些库,例如 distutils.version 来完成这项工作。 你应该看看answer。

【讨论】:

如果正则表达式明显快于拆分,我会感到惊讶。 感谢@MartijnPieters 刚查过,分体版速度更快。 2.33 我们与 0.732 我们。 @HannesOvrén 很好奇,关于我上次的编辑,显然我真的没有相同的结果。你是怎么比较的? 我在 IPython 中使用了 %timeit 魔法。然而,我确实用res = re.match(pat, version) 而不是res = pat.match(version) 调用了正则表达式。后者使它们或多或少具有相同的速度(884 ns)。【参考方案2】:

回答您的第一个问题:不,这不会被解释器优化。 Python 将从字符串创建一个列表,然后为切片创建第二个列表,然后将列表项重新组合成一个新字符串。

为了涵盖第二个,您可以通过使用可选的maxsplit 参数限制split 来稍微优化:

>>> v = '1.2.3.4.5-RC4'
>>> v.split(".", 3)
['1', '2', '3', '4.5-RC4']

一旦找到第三个'.',Python 就会停止搜索字符串。您还可以通过删除切片的默认 0 参数来稍微整理一下:

api = '.'.join(version.split('.', 3)[:3])

但是请注意,性能上的任何差异都可以忽略不计:

>>> import timeit
>>> def test1(version):
    return '.'.join(version.split('.')[0:3])

>>> def test2(version):
    return '.'.join(version.split('.', 3)[:3])

>>> timeit.timeit("test1(s)", setup="from __main__ import test1, test2; s = '1.2.3.4.5-RC4'")
1.0458565345561743
>>> timeit.timeit("test2(s)", setup="from __main__ import test1, test2; s = '1.2.3.4.5-RC4'")
1.0842980287537776

maxsplit 的好处随着包含更多不相关'.'s 的更长字符串变得更加明显:

>>> timeit.timeit("s.split('.')", setup="s='1.'*100")
3.460900054011617
>>> timeit.timeit("s.split('.', 3)", setup="s='1.'*100")
0.5287887450379003

【讨论】:

您对性能提升的数量有什么看法吗? @Wolf 添加;显然没有区别! 谢谢,有一个略有增加 - 但不是简单;-) ...我最好将maxsplit 好处格式化为脚注【参考方案3】:

我习惯用C/C++写代码,看到下面的数组操作,感觉有点浪费CPU:

感觉 CPU 浪费对于面对 python 代码的 C/C++ 程序员来说是绝对正常的。你的代码:

version = '1.2.3.4.5-RC4'                 # the end can vary a lot
api = '.'.join(version.split('.')[0:3])   # extract '1.2.3'

在python中绝对没问题不可能简化。只有如果你必须这样做1000次,考虑using a library function或write your own。

【讨论】:

以上是关于简化语句 '.'.join(string.split('.')[0:3] )的主要内容,如果未能解决你的问题,请参考以下文章

深度长文探讨JOIN运算的简化和提速

深度长文探讨JOIN运算的简化和提速

深度长文探讨JOIN运算的简化和提速

os.path.join 与 import 语句[重复]

sql语句优化

sql语句优化