2011-11-26 33 views
6

Bazı StringIO (cStringIO dan) varsayalım. Ondan tampon okunan bazı karakter/bayt ile karşılaşana kadar istiyoruz, bu yüzden, 'Z' demek:hızlı yolu

stringio = StringIO('ABCZ123') 
buf = read_until(stringio, 'Z') # buf is now 'ABCZ' 
# strinio.tell() is now 4, pointing after 'Z' 

Python bunu yapmanın en hızlı yolu nedir?

cevap

4

bunu ilginç ve ilgili soru olduğu için bu soru, yığın taşması üzerinde sadece bir cevap almak hayal kırıklığına. Bu (belki kömürü sona ilk yığın bulunamadı) parçalar halinde io okumak

def foo(stringio): 
    datalist = [] 
    while True: 
     chunk = stringio.read(256) 
     i = chunk.find('Z') 
     if i == -1: 
      datalist.append(chunk) 
     else: 
      datalist.append(chunk[:i+1]) 
      break 
     if len(chunk) < 256: 
      break 
    return ''.join(datalist) 

: Neyse, sadece ovgolovin çözüm vermek ve bunu belki yavaş thinked beri, daha hızlı bir çözüm düşündüm. Çok hızlıdır çünkü her karakter için Python işlevi yoktur, aksine C-yazılı Python işlevlerinin maksimum kullanımı.

Bu, yaklaşık ovgolovin'in çözümünden 60 kat daha hızlı çalışır. Kontrol etmek için timeit'u çalıştırdım.

+0

Çok iyi bir çözüm! Python'un ağır yükünü işlev çağrıları üzerine giderir. Tek dezavantajı, bellekte bir "datalist" nesnesini saklaman. Bu kodu işlev yerine jeneratörle yeniden yazmak mümkündür ('join 'yineleyicileri kabul eder), böylece bellekte geçici yedek nesneler olmayacaktır. – ovgolovin

+0

Ancak jeneratör sürümü biraz daha yavaş çıkıyor: http://ideone.com/dQGe5 (Bir dizge büyükse (1 milyon sembol) - daha sonra jeneratör versiyonu biraz daha hızlıdır). – ovgolovin

+0

Bu arada, neden '256' sembolü parçaları seçtiniz? (Neden 512' veya '1024' değil?) – ovgolovin

2
i = iter(lambda: stringio.read(1),'Z') 
buf = ''.join(i) + 'Z' 

İşte iter bu modda kullanıldığında teşekkür ederiz: iter(callable, sentinel) -> iterator.

''.join(...) oldukça etkilidir. 'Z' ''.join(i) + 'Z' eklemenin son işlemi bu kadar iyi değil. Ama Yineleyici için 'Z' ekleyerek ele alınabilir:

from itertools import chain, repeat 

stringio = StringIO.StringIO('ABCZ123') 
i = iter(lambda: stringio.read(1),'Z') 
i = chain(i,repeat('Z',1)) 
buf = ''.join(i) 

Bir daha yolu yapmak için bu jeneratör kullanmaktır: Bazı verimlilik testleri yapılmış

def take_until_included(stringio): 
    while True: 
     s = stringio.read(1) 
     yield s 
     if s=='Z': 
      return 

i = take_until_included(stringio) 
buf = ''.join(i) 

. anlatılan tekniklerin performansı oldukça aynıdır:

http://ideone.com/dQGe5

+0

Ancak "Z", akıştan alınmadı, ya da değil mi? – zaharpopov

+0

@zaharpopov Hayır, düştü. Bu yüzden, bu soruna hitap etmek için + 'Z' ve 'zinciri (i, tekrarla ('Z', 1))' kullandım. Bir sentinel olarak ne kullandığımızı biliyoruz, bu yüzden akışı kolayca manuel olarak ekleyebiliriz. Sizin çaba için – ovgolovin

+0

Спасибо, ama cevabımı gör – zaharpopov