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 * 100untuk foomendapatkan 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 findpanggilan, 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 splitlinesdiimplementasikan 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 .findpendekatan 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 f3atas, 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 f4adalah:
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 \nsayangnya melarang penggantian whileloop yang lebih jelas dan lebih cepat dengan return iter(stri)( iterbagian 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 stripberbasis, paling sederhana dan tercepat.
foo.splitlines()kan?