SORU
16 HAZİRAN 2010, ÇARŞAMBA


Yineleme bir dize hatları

Çok satırlı bir dize böyle tanımlıyorum:

foo = """
this is 
a multi-line string.
"""

Bu dize yazıyorum bir çözümleyici için test giriş olarak bizi kullandı. Parser-function file-nesne girdi olarak alır ve yineler. next() Bu yöntem, doğrudan satır atlamak için çağrı da, gerçekten girdi, bir iterable gibi bir yineleyici. file-nesne gibi dize tek tek satırları üzerinde yineleme metin dosyası hatları bir yineleyici ihtiyacım var. Elbette bu böyle olabilir:

lineiterator = iter(foo.splitlines())

Bunun daha kestirme bir yolu var mı? Bu senaryoda dize bir kez bölme için geçiş ve sonra tekrar çözümleyici tarafından. Test-case, ip çok kısa olduğu için, sadece meraktan soruyorum benim için önemli değil. Python çok yararlı ve verimli built-ins böyle şeyler için vardır, ama bu ihtiyaca uygun bir şey bulamadım.

CEVAP
16 HAZİRAN 2010, ÇARŞAMBA


Burada üç ihtimal var:

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())

Ana senaryo olarak çalışan bu üç fonksiyon eşdeğer olduğunu doğruladı. timeit (ve daha kesin ölçüm için önemli dizeleri almak için foo * 100):

$ 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

Bu kullanımına, sadece inşa geçiş sağlamak için list() çağrı ihtiyacımız var unutmayın.

ALÇAK, saf uygulama çok daha hızlı değil hatta komik: 6 kat daha hızlı, daha denemem ile find aramalar, devre 4 kat daha hızlı daha alt düzey bir yaklaşım.

Ders korur: ölçüm her zaman iyi bir şey olmalı ama doğru); string yöntemleri gibi splitlines uygulandığını çok hızlı yol; koyarak dizeleri birlikte programlama bir çok düşük seviye (esp. çok küçük parçalar) = döngüler oldukça yavaş olabilir.

Edit: @Jacob önerisi, biraz diğerleri gibi aynı sonuçları (bir satır boşluk tutulur) değiştirilmiş, yani: eklendi

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

oldukça iyi olarak .find dayalı bir yaklaşım hala, değer tutmak zihin, çünkü bu belki daha az eğilimli küçük devre dışı-tarafından bir hata (herhangi bir döngü nerede görüyorsunuz tekrarı 1 ve -1, benim gibi f3 yukarıda, gereken otomatik olarak tetikten tek kuşkular ve gereken birçok döngüler eksikliği bu tür uygulamalar olmalı ... ama sanırım benim kod da doğru yaşımdan beri mümkün için onay çıktı diğer fonksiyonlar').

Ama split merkezli kurallar yaklaşım.

f4 tarzı olurdu belki daha iyi: kenara bir:

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ı. İhtiyaç striptiz izleyen \ns ne yazık ki yasaklar daha net ve daha hızlı değiştirme while döngü ile return iter(stri) (iter bölüm rakamın gereksiz modern sürümleri Python, inanıyorum beri 2.3 veya 2.4, ama aynı zamanda zararsız). Denemeye değer belki de

    return itertools.imap(lambda s: s.strip('\n'), stri)

ya da çeşitleri -- ama çok fazla strip temel, en basit ve en hızlı bir wrt teorik bir çalışma olduğu için burada kesiyorum.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Kurtindo Pop Games

    Kurtindo Pop

    2 HAZİRAN 2013
  • kylediablo

    kylediablo

    8 Ocak 2007
  • Kyletiv7

    Kyletiv7

    28 Mayıs 2007