Идиома Python: понимание списка с ограничением элементов - PullRequest
5 голосов
/ 21 августа 2010

Я в основном пытаюсь сделать это (псевдокод, не действительный python):

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

Итак, как я могу написать это кратко и эффективно? XML-файл может содержать от 0 до 50 исполнителей, и я не могу контролировать, сколько их можно получить за раз, и AFAIK, здесь нет выражения XPATH, которое бы говорило что-то вроде «получи мне до 10 узлов».

Спасибо!

Ответы [ 5 ]

6 голосов
/ 21 августа 2010

Вы используете lxml? Вы можете использовать XPath для ограничения элементов на уровне запроса, например

>>> 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']

Вы также можете использовать itertools.islice для ограничения любой итерации , например

>>> 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']
5 голосов
/ 21 августа 2010

Предполагая, что xml является объектом ElementTree, метод findall() возвращает список, поэтому просто нарежьте этот список:

limit = 10
limited_artists = xml.findall('artist')[:limit]
results = [xml_to_dict(artist) for artist in limited_artists]
3 голосов
/ 11 мая 2014

Для всех остальных, кто нашел этот вопрос, потому что пытался ограничить количество элементов, возвращаемых бесконечным генератором:

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 ]
2 голосов
/ 21 августа 2010

Это позволяет избежать проблем среза: он не меняет порядок операций и не создает новый список, который может иметь значение для больших списков, если вы фильтруете понимание списка.

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

Он также корректно работает с выражениями генератора, где срез будет зависать из-за использования памяти:

exp = (i for i in first(xrange(1000000000), 10000000))
for i in exp:
    print i
2 голосов
/ 21 августа 2010
limit = 10
limited_artists = [artist in xml.findall('artist')][:limit]
results = [xml_to_dict(artist) for limited_artists]
...