2016-08-10 39 views
0

Я ищу что-то вроде:Как вернуть первые п элементов итератора, которые проходят испытание без выполнения теста на остаток

foo = [ p for p in somelist if xxx(p)][0:10] 

Это работает, но выполняет ххх на все somelist, и xxx стоит дорого.

Я мог бы написать подфункции, такие как

def blah (list, len): 
    res=[]; i=0; 
    while i<len: 
     if xxx(list[i]): 
      res.append(i) 
    return res 

, но это, кажется, очень unpythonic.

Примером, подтверждающим первый результат делает неправильно, что будет:

foo = [ p for p in (1,2,3,4,5,0,6) if 100/p ][0:3] 

, которые «должны» вернуться [1,2,3], но на самом деле не будет работать на деление на ноль.

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

ответ

2

Попробуйте itertools.islice:

from itertools import islice 
foo = list(islice((p for p in (1,2,3,4,5,0,6) if 100/p), 4)) 

Обратите внимание на отсутствии скобок: это generator comprehension

+0

Угловые скобки: '<>'. '[]' Одни называются квадратными скобками или просто скобками в зависимости от того, откуда вы. – user2357112

+0

@ user2357112: очень верно, спасибо – user357269

1

Это невозможно сделать с списковыми но можно с постижениями генератора.

В чем разница между этими двумя? Перечисления списков будут перебирать все повторяющиеся все и возвращать вам список в соответствии с вашими операциями над элементами в итерабельном. Ключ здесь все сразу.

В этом примере используются проверки списков, и вот что происходит: сначала оценивается понимание списка. В вашем случае это не удастся, но даже если это не сработает, он будет переименован во все в итерабельном somelist и вернет результирующий список обратно. Затем этот список нарезается и возвращается новый результирующий список.

Генераторы и генераторы вообще имеют разные способы работы. Они представляют собой в основном блоки кода, которые приостановлены до тех пор, пока вы не запросите у них больше данных, что вы действительно хотите сделать.

В сущности, вы создаете генератор следующим образом:

g = (p for p in (1,2,3,4,5,0,6) if 100/p) 

Теперь у вас есть генератор, который будет генерировать значения для вас, когда вы запроса это сделать так, согласно «правилу» вы дали Это.

Как только у вас есть генератор под рукой, есть несколько способов получить из него n элементов.

Вы могли бы написать простой цикл следующим образом:

results = [] 
for x in range(3): # n = 3 in your case 
    results.append(next(g)) 

Конечно, это не Pythonic.Предполагая, что вы хотите получить список назад, теперь вы можете использовать список понимание:

results = [next(g) for x in range(3)] 

Это «ручной» способ сделать это. Вы можете также использовать функцию islice из модуля (Documentation here)itertools:

import itertools 
results = list(itertools.islice(g, 4)) 

И там. Генераторы очень полезны. Блок кода, который выполняется при запросе и запоминании его состояния, действительно бесценен.

0

Используя комбинацию встроенного фильтра и islice, вы можете достичь того, чего хотите. например.

length = 3 
items_iter = islice(filter(None, [1, 2, 0, 4, 5]), length) # returns a lazy iterator 
items = list(items_iter) 
assert items == [1, 2, 5] 

Однако вы можете написать свой собственный генератор. Функции генератора, вместо того, чтобы возвращать один результат, будут давать последовательные элементы. Иногда он не может давать никаких предметов, и иногда они могут давать бесконечное количество предметов. Вот пример вашего дела:

def take_first_n_that_pass(test, length, iterator): 
    i = 0 
    for item in iterator: 
     if i >= length: 
      return # stops generator 
     if test(item): 
      yield item 
      i += 1 
    # end of iterator -- generator stops here as well 

items_iter = take_first_n_that_pass(bool, 3, [0, 1, 2, 0, 4, 5]) 
items = list(items_iter) 
assert items == [1, 2, 4]