2010-08-20 7 views
3

Je suis fondamentalement essayer de le faire (pseudo code, pas python valide):langage Python: la compréhension de la liste avec la limite des articles

limit = 10 
results = [xml_to_dict(artist) for artist in xml.findall('artist') while limit--] 

Alors, comment pourrais-je coder cela d'une manière concise et efficace? Le fichier XML peut contenir n'importe quoi entre 0 et 50 artistes, et je ne peux pas contrôler le nombre à obtenir à la fois, et AFAIK, il n'y a pas d'expression XPATH pour dire quelque chose comme "10 nœuds".

Merci!

Répondre

5

En supposant que xml est un objet ElementTree, la méthode findall() retourne une liste, si juste tranche cette liste:

limit = 10 
limited_artists = xml.findall('artist')[:limit] 
results = [xml_to_dict(artist) for artist in limited_artists] 
+0

+1: Pour être plus rapide. –

2
limit = 10 
limited_artists = [artist in xml.findall('artist')][:limit] 
results = [xml_to_dict(artist) for limited_artists] 
+0

+1 pour synchronicité! – jathanism

2

Cela évite les problèmes de découpage en tranches: il ne change pas l'ordre de opérations, et ne construit pas une nouvelle liste, ce qui peut importer pour les grandes listes si vous filtrez la compréhension de la liste.

def first(it, count): 
    it = iter(it) 
    for i in xrange(0, count): 
     yield next(it) 
    raise StopIteration 

print [i for i in first(range(1000), 5)] 

Il fonctionne aussi bien avec des expressions du générateur, où le découpage va tomber en raison de l'utilisation de la mémoire:

exp = (i for i in first(xrange(1000000000), 10000000)) 
for i in exp: 
    print i 
+0

Vous n'avez pas vraiment ** besoin ** d'élever 'StopIteration'. Terminer simplement la fonction fera l'affaire. –

6

Utilisez-vous lxml? Vous pouvez utiliser XPath pour limiter les éléments au niveau de la requête, par ex.

>>> from lxml import etree 
>>> from io import StringIO 
>>> xml = etree.parse(StringIO('<foo><bar>1</bar><bar>2</bar><bar>4</bar><bar>8</bar></foo>')) 
>>> [bar.text for bar in xml.xpath('bar[position()<=3]')] 
['1', '2', '4'] 

Vous pouvez également use itertools.islice to limit any iterable, par exemple

>>> from itertools import islice 
>>> [bar.text for bar in islice(xml.iterfind('bar'), 3)] 
['1', '2', '4'] 
>>> [bar.text for bar in islice(xml.iterfind('bar'), 5)] 
['1', '2', '4', '8'] 
+0

Pensez-vous que la solution XPath est plus rapide que les solutions de découpage en tranches? Je pense que les éléments sont paresseux, mais il pourrait être juste plus rapide de ne pas obtenir les éléments du tout droit? Je ne sais pas – adamJLev

+0

@Infinity: Je pense que 'islice' est plus rapide car XPath est plus compliqué. Je n'ai pas vérifié cependant. Vous devez l'évaluer vous-même. – kennytm

2

Pour tous les autres qui ont trouvé cette question parce qu'ils essayaient de limiter les articles retournés à partir d'un générateur infini:

from itertools import takewhile 
ltd = takewhile(lambda x: x[0] < MY_LIMIT, enumerate(MY_INFINITE_GENERATOR)) 
#^This is still an iterator. 
# If you want to materialize the items, e.g. in a list, do: 
ltd_m = list(ltd) 
# If you don't want the enumeration indices, you can strip them as usual: 
ltd_no_enum = [ v for i,v in ltd_m ] 
Questions connexes