Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pick just one item from a generator?

People also ask

How do Python generators work?

A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.

Can you explain what a generator is in Python?

Python generators are a simple way of creating iterators. All the work we mentioned above are automatically handled by generators in Python. Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).

What is php generator?

A generator is basically a normal function, but instead of returning a value it yields as many values as it needs to. It looks like a function but acts like an iterator. Generators use the yield keyword instead of return .


Create a generator using

g = myfunct()

Everytime you would like an item, use

next(g)

(or g.next() in Python 2.5 or below).

If the generator exits, it will raise StopIteration. You can either catch this exception if necessary, or use the default argument to next():

next(g, default_value)

For picking just one element of a generator use break in a for statement, or list(itertools.islice(gen, 1))

According to your example (literally) you can do something like:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

If you want "get just one element from the [once generated] generator whenever I like" (I suppose 50% thats the original intention, and the most common intention) then:

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

This way explicit use of generator.next() can be avoided, and end-of-input handling doesn't require (cryptic) StopIteration exception handling or extra default value comparisons.

The else: of for statement section is only needed if you want do something special in case of end-of-generator.

Note on next() / .next():

In Python3 the .next() method was renamed to .__next__() for good reason: its considered low-level (PEP 3114). Before Python 2.6 the builtin function next() did not exist. And it was even discussed to move next() to the operator module (which would have been wise), because of its rare need and questionable inflation of builtin names.

Using next() without default is still very low-level practice - throwing the cryptic StopIteration like a bolt out of the blue in normal application code openly. And using next() with default sentinel - which best should be the only option for a next() directly in builtins - is limited and often gives reason to odd non-pythonic logic/readablity.

Bottom line: Using next() should be very rare - like using functions of operator module. Using for x in iterator , islice, list(iterator) and other functions accepting an iterator seamlessly is the natural way of using iterators on application level - and quite always possible. next() is low-level, an extra concept, unobvious - as the question of this thread shows. While e.g. using break in for is conventional.


Generator is a function that produces an iterator. Therefore, once you have iterator instance, use next() to fetch the next item from the iterator. As an example, use next() function to fetch the first item, and later use for in to process remaining items:

# create new instance of iterator by calling a generator function
items = generator_function()

# fetch and print first item
first = next(items)
print('first item:', first)

# process remaining items:
for item in items:
    print('next item:', item)

You can pick specific items using destructuring, e.g.:

>>> first, *middle, last = range(10)
>>> first
0
>>> middle
[1, 2, 3, 4, 5, 6, 7, 8]
>>> last
9

Note that this is going to consume your generator, so while highly readable, it is less efficient than something like next(), and ruinous on infinite generators:

>>> first, *rest = itertools.count()
šŸ”„šŸ”„šŸ”„