2012-08-28 27 views
15

Bu, tüm kullanılabilir belleğimi tüketir ve sonra işlem öldürülür. Etiketi schedule'dan 'küçük' etiketlere değiştirmeyi denedim, ancak bu bir fark yaratmadı.Neden lxml.etree.iterparse() tüm belleğimi yiyor?

Neyi yanlış yapıyorum/bu büyük dosyayı iterparse() ile nasıl işleyebilirim?

import lxml.etree 

for schedule in lxml.etree.iterparse('really-big-file.xml', tag='schedule'): 
    print "why does this consume all my memory?" 

Kolayca kesebilir ve daha küçük parçalar halinde işleyebilirim ama istediğim kadar çirkin.

cevap

18

iterparse tüm dosya üzerinde bir ağaç oluşturulur ve hiçbir öğe serbest bırakılmaz. Bunu yapmanın avantajı, öğelerin ebeveynlerinin kim olduğunu hatırlaması ve ata öğelerinin öğelerini ifade eden XPath'lar oluşturabilmenizdir. Dezavantajı, çok fazla hafıza tüketebilmesidir.

def fast_iter(context, func, *args, **kwargs): 
    """ 
    http://lxml.de/parsing.html#modifying-the-tree 
    Based on Liza Daly's fast_iter 
    http://www.ibm.com/developerworks/xml/library/x-hiperfparse/ 
    See also http://effbot.org/zone/element-iterparse.htm 
    """ 
    for event, elem in context: 
     func(elem, *args, **kwargs) 
     # It's safe to call clear() here because no descendants will be 
     # accessed 
     elem.clear() 
     # Also eliminate now-empty references from the root node to elem 
     for ancestor in elem.xpath('ancestor-or-self::*'): 
      while ancestor.getprevious() is not None: 
       del ancestor.getparent()[0] 
    del context 

sonra böyle kullanabilirsiniz:

def process_element(elem): 
    print "why does this consume all my memory?" 
context = lxml.etree.iterparse('really-big-file.xml', tag='schedule', events = ('end',)) 
fast_iter(context, process_element) 

ederim the article yukarıda fast_iter üzerinde tavsiye bazı bellek boşaltmak için

ayrıştırmak olarak, Liza Daly en fast_iter kullanmak dayanır; Büyük XML dosyaları ile uğraşıyorsanız özellikle ilgi çekici olmalıdır.

Yukarıda sunulan fast_iter, makalede görüntülenen 'un biraz değiştirilmiş bir sürümüdür. Bu bir önceki ataları silmeyle ilgili daha agresiftir, böylece daha fazla bellek tasarrufu sağlar.farkını gösteren Here you'll find a script.

+0

teşekkürler! Hem senin çözümün, hem de yeni eklediğim şey hile yapıyor gibi gözüküyor, ben ve diğer insanların hangisinin daha iyi bir çözüm olduğunu merak ediyorum. Bir fikrin var mı? –

+3

Çözüm çalışmalarınızı kapatır ve http://effbot.org/zone/element-iterparse.htm çözümünü yapmadı (hala tüm belleğimi yedim) –

+0

Teşekkür ederiz! Bu gerçekten işe yarayan bir versiyon. Liza Daly, effbot ve lxml resmi dokümanlarından versiyonlar benim için çok fazla bellek tasarrufu yapmadı. – fjsj

3

Doğrudan iterparse hala ayrıştırma gibi bir ağaç oluşturur http://effbot.org/zone/element-iterparse.htm

Not kopyalanmış ama ayrıştırılırken güvenle ağacın parçalarını yeniden düzenlemek ya da kaldırabilirsiniz. Örneğin, kısa sürede bunları işleme koyduktan olarak elementlerin kurtulabilirsiniz, büyük dosyaları ayrıştırmak için:

for event, elem in iterparse(source): 
    if elem.tag == "record": 
     ... process record elements ... 
     elem.clear() 

yukarıdaki model bir dezavantajı var; kök öğesini temizlemez, böylece çok sayıda boş alt öğe içeren tek bir elementle sonuçlanırsınız. Dosyalarınız sadece büyük değil, büyük ise, bu bir sorun olabilir. Bu konuda çalışmak için ellerinizi kök öğesinde almanız gerekir. Bunu yapmanın en kolay yolu, başlangıç ​​olayları etkinleştirmek ve bir değişkene ilk öğeye bir başvuru kurtarmaktır:

# get an iterable 
context = iterparse(source, events=("start", "end")) 

# turn it into an iterator 
context = iter(context) 

# get the root element 
event, root = context.next() 

for event, elem in context: 
    if event == "end" and elem.tag == "record": 
     ... process record elements ... 
     root.clear() 
0

Bu benim için gerçekten iyi çalıştı:

def destroy_tree(tree): 
    root = tree.getroot() 

    node_tracker = {root: [0, None]} 

    for node in root.iterdescendants(): 
     parent = node.getparent() 
     node_tracker[node] = [node_tracker[parent][0] + 1, parent] 

    node_tracker = sorted([(depth, parent, child) for child, (depth, parent) 
          in node_tracker.items()], key=lambda x: x[0], reverse=True) 

    for _, parent, child in node_tracker: 
     if parent is None: 
      break 
     parent.remove(child) 

    del tree 
İlgili konular