İşte üç olasılık:
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())
Bunu ana komut dosyası olarak çalıştırmak, üç işlevin eşdeğer olduğunu doğrular. İle timeit(ve bir * 100için foodaha kesin ölçüm için önemli dizeleri almak için):
$ 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()Yineleyicilerin yalnızca oluşturulmasını değil, çaprazlanmasını sağlamak için çağrıya ihtiyacımız olduğunu unutmayın .
IOW, saf uygulama o kadar hızlı ki komik bile değil: findÇağrılarla denememden 6 kat daha hızlı , bu da daha düşük seviyeli bir yaklaşımdan 4 kat daha hızlı.
Akılda tutulması gereken dersler: ölçüm her zaman iyi bir şeydir (ancak doğru olmalıdır); gibi dize yöntemleri splitlinesçok hızlı bir şekilde uygulanır; dizeleri çok düşük bir seviyede (özellikle +=çok küçük parçalardan oluşan döngülerle) programlayarak bir araya getirmek oldukça yavaş olabilir.
Düzenleme : @ Jacob'ın teklifi eklendi, diğerleri ile aynı sonuçları verecek şekilde biraz değiştirildi (bir satırdaki sondaki boşluklar tutulur), yani:
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
Ölçüm verir:
$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop
.findtemelli yaklaşım kadar iyi değil - yine de akılda tutmaya değer çünkü tek tek küçük hatalara daha az eğilimli olabilir ( f3yukarıdaki gibi +1 ve -1 oluşumlarını gördüğünüz herhangi bir döngü otomatik olarak Tek tek şüpheleri tetikler - ve bu tür ince ayarlardan yoksun olan ve bunlara sahip olması gereken birçok döngü de gerekir - ancak kodumun da çıktısını diğer işlevlerle kontrol edebildiğim için doğru olduğuna inanıyorum ').
Ancak bölünmüş tabanlı yaklaşım hala geçerli.
Bir kenara: muhtemelen daha iyi bir stil f4:
from cStringIO import StringIO
def f4(foo=foo):
stri = StringIO(foo)
while True:
nl = stri.readline()
if nl == '': break
yield nl.strip('\n')
en azından biraz daha az ayrıntılı. Takip eden s'leri sıyırma ihtiyacı \nmaalesef whiledöngünün daha net ve daha hızlı değiştirilmesini yasaklıyor return iter(stri)(bu iterkısım Python'un modern sürümlerinde gereksizdir, inanıyorum ki 2.3 veya 2.4'ten beri ama aynı zamanda zararsızdır). Belki de denemeye değer:
return itertools.imap(lambda s: s.strip('\n'), stri)
veya bunların varyasyonları - ama burada duruyorum çünkü bu temelde strip, en basit ve en hızlı olanı olan teorik bir alıştırma .
foo.splitlines()değil mi?