Dưới đây là ba khả năng:
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())
Chạy này như kịch bản chính khẳng định ba chức năng tương đương. Với timeit
(và một * 100
cho foo
để có được chuỗi đáng kể để đo chính xác hơn):
$ 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
Lưu ý chúng ta cần các list()
gọi để đảm bảo vòng lặp nằm ngang, không chỉ xây dựng.
IOW, việc triển khai ngây thơ nhanh hơn rất nhiều, thậm chí còn nhanh hơn 6 lần so với nỗ lực của tôi với các cuộc gọi find
, nhanh gấp 4 lần so với phương pháp tiếp cận cấp thấp hơn.
Bài học cần lưu giữ: đo lường luôn là điều tốt (nhưng phải chính xác); các phương thức chuỗi như splitlines
được triển khai theo các cách rất nhanh; đặt các chuỗi lại với nhau bằng cách lập trình ở mức rất thấp (đặc biệt là bởi các vòng +=
các phần rất nhỏ) có thể khá chậm.
Chỉnh sửa: được thêm vào đề xuất của Jacob, được sửa đổi một chút để cung cấp kết quả tương tự như những người khác (dấu trống trên một hàng được giữ), tức là:
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
đo cho:
$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop
không hoàn toàn tốt như cách tiếp cận dựa .find
- vẫn còn, đáng ghi nhớ vì nó có thể là ít bị nhỏ off-by-one lỗi (bất kỳ vòng lặp nào mà bạn thấy các lần xuất hiện +1 và -1, giống như số f3
ở trên của tôi, sẽ tự động kích hoạt các nghi ngờ ngoại tuyến - và vì vậy nhiều vòng lặp thiếu các điều chỉnh như vậy và phải có chúng - mặc dù tôi tin rằng mã của tôi cũng ngay từ khi tôi có thể kiểm tra đầu ra của nó với các chức năng khác ').
Tuy nhiên, cách tiếp cận dựa trên chia tách vẫn còn quy tắc.
Một sang một bên: phong cách thể tốt hơn cho f4
sẽ là:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl == '': break
yield nl.strip('\n')
ít nhất, đó là một chút ít tiết. Sự cần thiết phải loại bỏ dấu vết \n
không may thay thế rõ ràng và nhanh hơn vòng lặp while
với return iter(stri)
(phần iter
mà không cần thiết trong các phiên bản Python hiện đại, tôi tin từ 2.3 hoặc 2.4, nhưng nó cũng vô hại). Có lẽ đáng để thử, cũng:
return itertools.imap(lambda s: s.strip('\n'), stri)
hoặc các biến thể của nó - nhưng tôi dừng ở đây vì nó khá nhiều bài tập lý thuyết wrt các strip
dựa, đơn giản nhất và nhanh nhất, người ta.
bạn biết rằng bạn có thể duyệt qua 'foo.splitlines() 'phải không? – SilentGhost
Bạn có ý nghĩa gì bởi "một lần nữa bởi trình phân tích cú pháp"? – danben
@SilentGhost: Tôi nghĩ rằng vấn đề là không lặp lại chuỗi hai lần. Khi nó được lặp lại bởi 'splitlines()' và lần thứ hai bằng cách lặp qua kết quả của phương thức này. –