这是三种可能性:
foo = """
this is
a multi-line string.
"""
def f1(foo=foo): return iter(foo.splitlines())
def f2(foo=foo):
retval = ''
for char in foo:
retval += char if not char == '\n' else ''
if char == '\n':
yield retval
retval = ''
if retval:
yield retval
def f3(foo=foo):
prevnl = -1
while True:
nextnl = foo.find('\n', prevnl + 1)
if nextnl < 0: break
yield foo[prevnl + 1:nextnl]
prevnl = nextnl
if __name__ == '__main__':
for f in f1, f2, f3:
print list(f())
将其作为主要脚本运行,确认这三个功能等效。使用timeit
(并使用* 100
for foo
获得大量字符串以进行更精确的测量):
$ python -mtimeit -s'import asp' 'list(asp.f3())'
1000 loops, best of 3: 370 usec per loop
$ python -mtimeit -s'import asp' 'list(asp.f2())'
1000 loops, best of 3: 1.36 msec per loop
$ python -mtimeit -s'import asp' 'list(asp.f1())'
10000 loops, best of 3: 61.5 usec per loop
注意,我们需要list()
调用以确保遍历迭代器,而不仅仅是构建迭代器。
IOW,天真的实现要快得多,甚至都不有趣:比我尝试find
调用快6倍,而调用比底层方法快4倍。
经验教训:测量永远是一件好事(但必须准确);像这样的字符串方法splitlines
以非常快的方式实现;通过在非常低的级别上进行编程(尤其是通过+=
非常小的片段的循环)来将字符串组合在一起可能会非常慢。
编辑:添加了@Jacob的提案,对其进行了稍微修改以使其与其他提案具有相同的结果(保留行尾空白),即:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl != '':
yield nl.strip('\n')
else:
raise StopIteration
测量得出:
$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop
不如.find
基于方法的方法好-仍然要牢记,因为它可能不大可能出现小的一次性错误(如f3
上面所述,任何出现+1和-1的循环都应该自动触发一个个的怀疑-许多循环应该缺少这些调整并且应该进行调整-尽管我相信我的代码也是正确的,因为我能够使用其他函数检查其输出')。
但是基于拆分的方法仍然占主导地位。
顺便说一句:可能更好的样式f4
是:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl == '': break
yield nl.strip('\n')
至少,它不那么冗长。\n
不幸的是,需要去除尾随s禁止使用来更清楚,更快速地替换while
循环return iter(stri)
(iter
在现代版本的Python中,多余的部分是多余的,我相信从2.3或2.4开始,但它也是无害的)。也许也值得尝试:
return itertools.imap(lambda s: s.strip('\n'), stri)
或其变体-但我在这里停止,因为这几乎是strip
基础,最简单和最快的一项理论练习。
foo.splitlines()
吗?