2011-02-09 31 views
37

bu senaryoyu düşünün: Bu örnek, tür gereksiz olduğunu biliyoruzPython jeneratör nesnesinin kopyası nasıl yapılır?

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import os 

walk = os.walk('/home') 

for root, dirs, files in walk: 
    for pathname in dirs+files: 
     print os.path.join(root, pathname) 

for root, dirs, files in walk: 
    for pathname in dirs+files: 
     print os.path.join(root, pathname)

ama biz birden fazla kez aynı walk verileri kullanmak gerektiğini düşünmelisiniz. Bir değerlendirme senaryosu ve yararlı sonuçlar elde etmek için aynı walk verilerinin kullanılması zorunludur.

İkinci yinelemede klonlamak ve kullanmak için walk2 = walk denedim, ancak işe yaramadı. Soru şu ... Nasıl kopyalayabilirim? Bu mümkün mü?

Önceden teşekkür ederiz.

+0

İki kez? Os.walk ('/ home') 'kullanmada sorun nedir? Bu nasıl bir problem? –

+2

@ S.Lott Peki, bu tür görevler her koşuda çok farklı. Bir başka problem ise, ilk çalıştırmadan sonra sistemin muhtemelen sonuçları önbelleğe alacağıdır, bu yüzden sonraki çalışmalarda benzersiz sonuçlar elde edeceğiz. Fikir, daha önce yürümeli ve argüman olarak geçen iki senaryoyu ölçmelidir. :) –

+0

Önbellekleme, yanlış sonuçlara neden olmaz. –

cevap

54

Sen itertools.tee() kullanabilirsiniz: dokümantasyon belirttiğine göre bu, "önemli ekstra depolama alanına ihtiyaç" olabilir

walk, walk2 = itertools.tee(walk) 

Not söyledi.

+6

da, [belgeler] (http://docs.python.org/2/library/itertools.html#itertools.tee) diyor ki: "Genel olarak, bir yineleyici başka bir yineleyici başlamadan önce verilerin çoğunu veya tümünü kullanıyorsa 'tee() 'yerine' list() 'kullanmak daha hızlıdır. OP'nin orijinal kod pasajı bir kez tamamen yinelenir ve tekrar tekrar verilirse, 'list() 'yi kullanması tavsiye edilmez mi? – HorseloverFat

+0

Bunun yerine önbelleğe alınmış bir üreteç kullanın, örneğin, burada (http://stackoverflow.com/a/21315536/1959808) açıklandığı gibi "lambda: a_new_generator". –

+1

Ayrıca bkz. [Bu yanıt] için yorumlar (http://stackoverflow.com/a/1271481/1959808). –

4

def walk_home(): 
    for r in os.walk('/home'): 
     yield r 

Hatta bu

def walk_home(): 
    return os.walk('/home') 

Hem böyle kullanılır bir işlevi tanımlayın: sizin için bütün jeneratör arasında yineleme olacak biliyorsanız

for root, dirs, files in walk_home(): 
    for pathname in dirs+files: 
     print os.path.join(root, pathname) 
+1

OP'nin sorduğu kesin soruya cevap olmasa da, bu, dizin dizin ağacını bellekte saklamaksızın yapmak için iyi bir yoldur. +1 –

+3

Döngü gerekli değil. walk_home(): dönüş os.walk ('/ home') 'aynı şeyi yapar. – shang

+0

@Sven Marnach: "Tam" soru çok az anlam ifade ediyor. –

12

Her kullanımda, jeneratörü bir listeye kaydırarak ve listeyi kullanarak en iyi performansı elde edersiniz. bir kaç sefer.

walk = list(os.walk('/home'))

1

Bu cevap diğer cevaplar ifade ettiği anlamın üzerine/ayrıntılı uzatmak amaçlamaktadır. Çözüm, elde etmeyi amaçladığınız tam olarak'a bağlı olarak değişecektir.

Eğer os.walk birden çok kez tam olarak aynı sonucu üzerinde yineleme yapmak istiyorsanız, os.walk bir liste başlatmak gerekecektir iterable en öğeler (örneğin walk = list(os.walk(path))).

Verilerin aynı kalmasını garanti etmeniz gerekiyorsa, muhtemelen tek seçeneğiniz budur. Bununla birlikte, bunun mümkün olmadığı veya arzu edilmediği çeşitli senaryolar vardır. Çıktı (yani bütün bir dosya sistemi bilgisayarınızı dondurabilir list() teşebbüs) yeterli büyüklükte ise

  1. O list() bir iterable mümkün olmayacaktır. Her kullanımdan önce "taze" veri elde etmek istiyorsanız, 'un yinelenebilir olması cazip değildir.

list()'un uygun olmaması durumunda, jeneratörü talep üzerine çalıştırmanız gerekir. Jeneratörlerin her kullanımdan sonra söndürüldüğünü unutmayın, bu yüzden bu küçük bir problem teşkil eder.Yukarıda belirtilen tasarım deseni size kod DRY tutmak sağlayacak

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import os 

class WalkMaker: 
    def __init__(self, path): 
     self.path = path 
    def __iter__(self): 
     for root, dirs, files in os.walk(self.path): 
      for pathname in dirs + files: 
       yield os.path.join(root, pathname) 

walk = WalkMaker('/home') 

for path in walk: 
    pass 

# do something... 

for path in walk: 
    pass 

: "tekrar yayınlama" senin jeneratör birden çok kez için, aşağıdaki modeli kullanabilirsiniz.

İlgili konular