2014-01-04 1 views
1

Что такое Pythonic способ проверить, является ли длина итераций не менее n?Питонический путь для длины генератора не менее n

Это мой подход:

import itertools 

def is_iterable_longer_than(iterable, n): 
    return n <= len(itertools.islice(iterable, n)) 

Есть ли что-нибудь лучше?

EDIT:

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

Как указано, вышесказанное имеет ошибку. Это должно быть:

return n <= len(list(itertools.islice(iterable, n))) 
+0

Невозможно сделать это, не оценивая по крайней мере значения 'n' от генератора. –

+0

@JoelCornett, я знал, что мне нужно будет оценить генератор. Я хотел узнать лучший способ. Очевидно, что я не хочу оценивать больше, чем необходимо, и было бы здорово не иметь промежуточных структур данных. И я бы хотел, чтобы он был очень ясным и довольно кратким. –

ответ

3

Нет общего пути. Ваш даже не работает:

>>> def is_iterable_longer_than(iterable, n): 
...  return n <= len(itertools.islice(iterable, n)) 
... 
>>> is_iterable_longer_than([], 2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in is_iterable_longer_than 
TypeError: object of type 'itertools.islice' has no len() 

Единственный способ сказать, если итератор имеет, по крайней мере, n объекты в нем перебрать его, пока вы не получите n объекты или выбежать. К сожалению, некоторые итерации могут повторяться только один раз. Если вы не заботитесь об использовании содержимого Iterable, вы можете сделать это:

def is_iterable_longer_than(iterable, n): 
    return n == sum(1 for _ in itertools.islice(iterable, n)) 

Если вам необходимо использовать содержимое из итерации, вы можете создать еще один Iterable, который выглядит как оригинал:

def is_iterable_longer_than(iterable, n): 
    iter1, iter2 = itertools.tee(iterable) 
    return sum(1 for _ in itertools.islice(iter1, n)) == n, iter2 

в то время как мы на это, мы могли бы также попробовать len, только в случае, если это работает:

def is_iterable_longer_than(iterable, n): 
    iter1, iter2 = itertools.tee(iterable) 
    try: 
     return len(iterable) >= n, iter2 
    except TypeError: 
     return sum(1 for _ in itertools.islice(iter1, n)) == n, iter2 
+0

@Nabla: Выпало 'islice'. Редактирование ... – user2357112

2

это зависит от того, вы готовы оценить генератор или нет.

Если вы, это тривиальное:

def gen_length_was_at_least_n(gen, n): 
    return n == sum(1 for _ in itertools.islice(gen, n)) 

Если это не так, то вы застряли, если вы не готовы оценить его, но сохранить значения в буфере с помощью tee:

gen, extra = itertools.tee(gen) 
if gen_length_was_at_least_n(extra): 
    # ... do something with gen 

Обратите внимание, что это делает частично оценивает исходный итератор; он просто сохраняет значения вокруг и обслуживает их через новый генератор, возвращенный с tee. Это означает, что если оценка генератора имеет побочные эффекты, они будут срабатывать, когда вы проведете проверку длины.