Why is there no first(iterable) built-in function in Python?

Question:

I’m wondering if there’s a reason that there’s no first(iterable) in the Python built-in functions, somewhat similar to any(iterable) and all(iterable) (it may be tucked in a stdlib module somewhere, but I don’t see it in itertools). first would perform a short-circuit generator evaluation so that unnecessary (and a potentially infinite number of) operations can be avoided; i.e.

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

This way you can express things like:

denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
    if all(i % denominators == 0 for denominator in denominators))

Clearly you can’t do list(generator)[0] in that case, since the generator doesn’t terminate.

Or if you have a bunch of regexes to match against (useful when they all have the same groupdict interface):

match = first(regex.match(big_text) for regex in regexes)

You save a lot of unnecessary processing by avoiding list(generator)[0] and short-circuiting on a positive match.

Asked By: cdleary

||

Answers:

In Python 2, if you have an iterator, you can just call its next method. Something like:

>>> (5*x for x in xrange(2,4)).next()
10

In Python 3, you can use the next built-in with an iterator:

>>> next(5*x for x in range(2,4))
10
Answered By: liori

Haskell makes use of what you just described, as the function take (or as the partial function take 1, technically). Python Cookbook has generator-wrappers written that perform the same functionality as take, takeWhile, and drop in Haskell.

But as to why that’s not a built-in, your guess is as good as mine.

Answered By: Mark Rushakoff

There’s some ambiguity in your question. Your definition of first and the regex example imply that there is a boolean test. But the denominators example explicitly has an if clause; so it’s only a coincidence that each integer happens to be true.

It looks like the combination of next and itertools.ifilter will give you what you want.

match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))
Answered By: A. Coady

There’s a Pypi package called “first” that does this:

>>> from first import first
>>> first([0, None, False, [], (), 42])
42

Here’s how you would use to return the first odd number, for example:

>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1)
7

If you just want to return the first element from the iterator regardless of whether is true or not, do this:

>>> first([0, None, False, [], (), 42], key=lambda x: True)
0

It’s a very small package: it only contains this function, it has no dependencies, and it works on Python 2 and 3. It’s a single file, so you don’t even have to install it to use it.

In fact, here’s almost the entire source code (from version 2.0.1, by Hynek Schlawack, released under the MIT licence):

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
Answered By: Flimm

I asked a similar question recently (it got marked as a duplicate of this question by now). My concern also was that I’d liked to use built-ins only to solve the problem of finding the first true value of a generator. My own solution then was this:

x = next((v for v in (f(x) for x in a) if v), False)

For the example of finding the first regexp match (not the first matching pattern!) this would look like this:

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)

It does not evaluate the predicate twice (as you would have to do if just the pattern was returned) and it does not use hacks like locals in comprehensions.

But it has two generators nested where the logic would dictate to use just one. So a better solution would be nice.

Answered By: Alfe

There is a “slice” iterator in itertools. It emulates the slice operations that we’re familiar with in python. What you’re looking for is something similar to this:

myList = [0,1,2,3,4,5]
firstValue = myList[:1]

The equivalent using itertools for iterators:

from itertools import islice
def MyGenFunc():
    for i in range(5):
        yield i

mygen = MyGenFunc()
firstValue = islice(mygen, 0, 1)
print firstValue 
Answered By: Zoran Pavlovic
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.