Đâ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 điều này như là kịch bản chính xác nhận ba chức năng là 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 ý rằng chúng ta cần lệnh list()
gọi để đảm bảo các trình vòng lặp được duyệt qua chứ không chỉ được xây dựng.
IOW, việc triển khai ngây thơ nhanh hơn rất nhiều, thậm chí không buồn cười: nhanh hơn 6 lần so với nỗ lực của tôi với find
các cuộc gọi, nhanh hơn 4 lần so với cách tiếp cận cấp thấp hơn.
Bài học cần lưu lại: đo lường luôn là một điều tốt (nhưng phải chính xác); các phương thức chuỗi như splitlines
được thực hiện theo những cách rất nhanh; việc đặ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ằng các vòng lặp của +=
các phần rất nhỏ) có thể khá chậm.
Chỉnh sửa : đã thêm đề xuất của @ Jacob, được sửa đổi một chút để cho kết quả tương tự như các đề xuất khác (các khoảng trống ở cuối trên một dòng được giữ lại), 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 lường mang lại:
$ 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ư .find
cách tiếp cận dựa trên - vẫn cần lưu ý vì nó có thể ít bị lỗi nhỏ hơn (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, như của tôi f3
ở trên, sẽ tự động kích hoạt từng sự nghi ngờ - và nhiều vòng lặp thiếu những tinh chỉnh như vậy cũng nên có - mặc dù tôi tin rằng mã của tôi cũng đúng vì tôi có thể kiểm tra đầu ra của nó với các chức năng khác ').
Nhưng cách tiếp cận dựa trên phân tách vẫn còn nguyên tắc.
Một bên: phong cách có thể tốt hơn 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, nó ít dài dòng hơn một chút. \n
Thật không may, nhu cầu loại bỏ dấu vết sẽ cấm việc thay thế while
vòng lặp rõ ràng và nhanh hơn bằng return iter(stri)
( iter
phần mà nó bị thừa trong các phiên bản Python hiện đại, tôi tin rằng kể từ phiên bản 2.3 hoặc 2.4, nhưng nó cũng vô hại). Cũng có thể đáng thử:
return itertools.imap(lambda s: s.strip('\n'), stri)
hoặc các biến thể của chúng - nhưng tôi đang dừng ở đây vì nó là một bài tập lý thuyết khá nhiều strip
, dựa trên một bài tập dựa trên, đơn giản nhất và nhanh nhất.
foo.splitlines()
đúng không?