Search code examples
pythoniteratorgenerator

Is the function "next" a good practice to find first occurrence in a iterable?


I've learned about iterators and such and discovered this quite interesting way of getting the first element in a list that a condition is applied (and also with default value in case we don't find it):

 first_occurence = next((x for x in range(1,10) if x > 5), None)

For me, it seems a very useful, clear way of obtaining the result.

But since I've never seen that in production code, and since next is a little more "low-level" in the python structure I was wondering if that could be bad practice for some reason. Is that the case? and why?


Solution

  • It's fine. It's efficient, it's fairly readable, etc.

    If you're expecting a result, or None is a possible result (so using None as a placeholder makes it hard to figure out if you got a result or got the default) it may be better to use the EAFP form rather than providing a default, catching the StopIteration it raises if no item is found, or just letting it bubble up if the problem is from the caller's input not meeting specs (so it's up to them to handle it). It looks even cleaner at point of use that way:

    first_occurence = next(x for x in range(1,10) if x > 5)
    

    Alternatively, when None is a valid result, you can use an explicit sentinel object that's guaranteed unique like so:

    sentinel = object()  # An anonymous object you construct can't possibly appear in the input
    first_occurence = next((x for x in range(1,10) if x > 5), sentinel)
    if first_occurence is not sentinel:  # Compare with is for performance and to avoid broken __eq__ comparing equal to sentinel
    

    A common use case for this one of these constructs to replace a call to any when you not only need to know if any item passed the test, but which item (any can only return True or False, so it's unsuited to finding which item passed).