Почему в Python нет первой (итерационной) встроенной функции?
мне интересно, есть ли причина, по которой нет first(iterable)
в Python встроены функции, несколько похожие на any(iterable)
и all(iterable)
(он может быть спрятан в модуле stdlib где-то, но я не вижу его в itertools
). first
будет выполнять оценку генератора короткого замыкания, чтобы можно было избежать ненужных (и потенциально бесконечного числа) операций; т. е.
def identity(item):
return item
def first(iterable, predicate=identity):
for item in iterable:
if predicate(item):
return item
raise ValueError('No satisfactory value found')
таким образом, вы можете выразить такие вещи, как:
denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
if all(i % denominators == 0 for denominator in denominators))
ясно, что вы не можете сделать list(generator)[0]
в этом случае, так как генератор не завершается.
или если у вас есть куча регулярных выражений, чтобы соответствовать против (полезно, когда все они имеют то же самое groupdict
интерфейс):
match = first(regex.match(big_text) for regex in regexes)
вы экономите много ненужной обработки, избегая list(generator)[0]
и короткое замыкание при положительном совпадении.
7 ответов:
Если у вас есть итератор, вы можете просто назвать его
next
метод. Что-то вроде:In [3]: (5*x for x in xrange(2,4)).next() Out[3]: 10
здесь пакет Pypi называется "первый" вот это:
>>> from first import first >>> first([0, None, False, [], (), 42]) 42
вот как вы бы использовали, чтобы вернуть первое нечетное число, например:
>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1) 7
если вы просто хотите вернуть первый элемент из итератора, независимо от того, правда это или нет, сделайте следующее:
>>> first([0, None, False, [], (), 42], key=lambda x: True) 0
это очень маленький пакет: он содержит только эту функцию, он не имеет зависимостей, и он работает на Python 2 и 3. Это один файл, так что у вас даже нет чтобы установить его, чтобы использовать его.
фактически, вот почти весь исходный код (от версии 2.0.1, hynek Schlawack, выпущенный по лицензии MIT):
def first(iterable, default=None, key=None): if key is None: for el in iterable: if el: return el else: for el in iterable: if key(el): return el return default
попросил аналогичный вопрос недавно (он был отмечен как дубликат этого вопроса к настоящему времени). Моя забота также заключалась в том, что мне нравилось использовать встроенные только для решения задачи нахождения первого истинного значения генератора. Мое собственное решение тогда было таково:
x = next((v for v in (f(x) for x in a) if v), False)
для примера нахождения первого совпадения регулярных выражений (а не первого совпадающего шаблона!) это будет выглядеть так:
patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ] text = 'abc' firstMatch = next( (match for match in (re.match(pattern, text) for pattern in patterns) if match), False)
Он не оценивает предикат дважды (как вы должны были бы сделать, если бы только шаблон был возвращен), и он не использует хаки, как местные жители в понимании.
но у него есть два вложенных генератора, где логика диктует использовать только один. Так что лучшее решение было бы неплохо.
в вашем вопросе есть некоторая двусмысленность. Ваше определение первый и пример регулярного выражения означает, что существует логический тест. Но пример знаменателей явно имеет предложение if; так что это только совпадение, что каждое целое число оказывается истинным.
это выглядит как комбинация next и itertools.ifilter даст вам то, что вы хотите.
match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))
в itertools есть итератор "slice". Он эмулирует операции среза, с которыми мы знакомы в python. То, что вы ищете что-то похожее на это:
myList = [0,1,2,3,4,5] firstValue = myList[:1]
эквивалент с использованием itertools для итераторов:
from itertools import islice def MyGenFunc(): for i in range(5): yield i mygen = MyGenFunc() firstValue = islice(mygen, 0, 1) print firstValue
Haskell использует то, что вы только что описали, как функцию
take
(или как частичная функцияtake 1
, технически). Python Cookbook имеет генератор-обертки написаны, которые выполняют те же функции, что иtake
,takeWhile
иdrop
в Haskell.но почему это не встроенный, ваше предположение столь же хорошо, как и мое.
вы можете использовать звездную распаковку, которая поддерживается в Python 3.x. вы должны прочитать этот PEP:https://www.python.org/dev/peps/pep-3132/
x = [0, 1, 2, 3] first, *rest = x print(first) print(rest)
Python перебирает список и присваивает каждый элемент переменным с левой стороны. Переменные со звездой впереди, принимает остальные как новый список.