Berikut tiga kemungkinan:
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())
Menjalankan ini sebagai skrip utama mengonfirmasi bahwa ketiga fungsi tersebut setara. Dengan timeit
(dan * 100
untuk foo
mendapatkan string substansial untuk pengukuran yang lebih tepat):
$ 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
Perhatikan bahwa kita membutuhkan list()
panggilan untuk memastikan iterator dilintasi, tidak hanya dibuat.
IOW, implementasi naif jauh lebih cepat bahkan tidak lucu: 6 kali lebih cepat daripada upaya saya dengan find
panggilan, yang pada gilirannya 4 kali lebih cepat daripada pendekatan tingkat yang lebih rendah.
Pelajaran yang perlu dipertahankan: pengukuran selalu merupakan hal yang baik (tetapi harus akurat); metode string seperti splitlines
diimplementasikan dengan cara yang sangat cepat; menempatkan string bersama-sama dengan memprogram pada tingkat yang sangat rendah (khususnya dengan loop dari +=
bagian yang sangat kecil) bisa sangat lambat.
Sunting : menambahkan proposal @ Jacob, sedikit dimodifikasi untuk memberikan hasil yang sama dengan yang lain (tanda kosong pada baris disimpan), yaitu:
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
Pengukuran memberi:
$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop
tidak sebagus .find
pendekatan berbasis - tetap saja, perlu diingat karena mungkin kurang rentan terhadap bug kecil-kecilan (setiap loop di mana Anda melihat kemunculan +1 dan -1, seperti yang saya di f3
atas, akan secara otomatis memicu kecurigaan off-by-one - dan seharusnya banyak loop yang tidak memiliki tweak seperti itu dan seharusnya memilikinya - meskipun saya yakin kode saya juga benar karena saya dapat memeriksa outputnya dengan fungsi lain ').
Tetapi pendekatan berbasis terpisah masih berlaku.
Sebuah tambahan: gaya yang mungkin lebih baik untuk f4
adalah:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl == '': break
yield nl.strip('\n')
setidaknya, itu sedikit kurang bertele-tele. Kebutuhan untuk menghapus jejak \n
sayangnya melarang penggantian while
loop yang lebih jelas dan lebih cepat dengan return iter(stri)
( iter
bagian yang berlebihan dalam versi modern Python, saya percaya sejak 2.3 atau 2.4, tetapi juga tidak berbahaya). Mungkin patut dicoba, juga:
return itertools.imap(lambda s: s.strip('\n'), stri)
atau variasinya - tetapi saya berhenti di sini karena ini adalah latihan teoretis yang strip
berbasis, paling sederhana dan tercepat.
foo.splitlines()
kan?