DEV Community

Super Kai (Kazuya Ito)
Super Kai (Kazuya Ito)

Posted on • Edited on

Generator in Python (1)

Buy Me a Coffee

*Memo:

  • My post explains a generator (2).
  • My post explains a generator (3).
  • My post explains a generator (4).
  • My post explains a generator comprehension.
  • My post explains an iterator (1).

A generator:

  • is the function which has one or more yield statements to return an iterator and whose type is generator:
  • 's iterator can be created by a generator comprehension:
    • A generator comprehension is an expression.
  • 's iterator cannot be copied.
  • can use throw(), close(), send(), gi_yieldfrom, gi_running and gi_suspended.
  • 's yield statement can be assigned to a variable.
  • 's yield statement cannot be assigned to a for statement.
  • terminates if there are no elements to return, if close() is called or of course if error occurs.

A generator can be created with a function and one or more yield statements and read with next() as shown below:

*Memo:

  • __next__() can also be used to read a generator.
  • A yield statements is a yield or yield from.
  • A yield can return any type of element.
  • A yield from can only return an iterable.

<yield>:

def gen():
    yield 0
    yield 1
    yield 2

print(gen)       # <function func at 0x000001FCD2FF93A0>
print(type(gen)) # <class 'function'>

v = gen()

print(v)       # <generator object func at 0x000001FCD3015220>
print(type(v)) # <class 'generator'>

print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode
def gen():
    yield [0, 1, 2]
    yield [3, 4, 5]

v = gen()

print(next(v)) # [0, 1, 2]
print(next(v)) # [3, 4, 5]
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

<yield from>:

def gen():
    yield from [0, 1, 2]
    yield from [3, 4, 5]

print(gen)       # <function func at 0x000001FCD640B1A0>
print(type(gen)) # <class 'function'>

v = gen()

print(v)       # <generator object func at 0x000001FCD661DD80>
print(type(v)) # <class 'generator'>

print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # 3
print(next(v)) # 4
print(next(v)) # 5
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode
def gen():
    yield from 0
    yield from 1
    yield from 2

v = gen()

print(next(v))
# TypeError: 'int' object is not iterable
Enter fullscreen mode Exit fullscreen mode

<yield & yield from>:

def gen():
    yield [0, 1, 2]
    yield from [3, 4, 5]

v = gen()

print(next(v)) # [0, 1, 2]
print(next(v)) # 3
print(next(v)) # 4
print(next(v)) # 5
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

This is how a generator works as shown below:

*Memo:

  • next() starts or resumes a generator, executes a yield statement to return a value and pauses the generator at a yield statement or raises StopIteration if a generator is terminated:
  • A generator is always paused at a yield statement.
  • __next__() also does the same things.

<yield>:

def gen():
    print('gen() starts.')
    yield 'gen() pauses.'

    print('gen() resumes.')
    yield 'gen() pauses again.'

    print('gen() resumes again.')
    yield 'gen() terminates.'

v = gen() # `gen()` doesn't start yet.

print(next(v))
# gen() starts.
# gen() pauses.

print(next(v))
# gen() resumes.
# gen() pauses again.

print(next(v))
# gen() resumes again.
# gen() terminates.

print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

<yield from>:

def gen():
    print("gen() starts.")
    yield from ["gen() pauses.",
                "gen() resumes and pauses."]

    print("gen() resumes.")
    yield from ["gen() pauses.",
                "gen() resumes and terminates."]

v = gen() # `gen()` doesn't start yet.

print(next(v))
# gen() starts.
# gen() pauses.

print(next(v))
# gen() resumes and pauses.

print(next(v))
# gen() resumes.
# gen() pauses.

print(next(v))
# gen() resumes and terminates.

print(next(v)) # StopIteration: 
Enter fullscreen mode Exit fullscreen mode

This is the generator with a for statement as shown below:

<yield>:

def gen():
    for x in [0, 1, 2]:
        yield x

v = gen()

print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode
def gen():
    for x in [[0, 1, 2], [3, 4, 5]]:
        yield x

v = gen()

print(next(v)) # [0, 1, 2]
print(next(v)) # [3, 4, 5]
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

<yield from>:

def gen():
    for x in [[0, 1, 2], [3, 4, 5]]:
        yield from x

v = gen()

print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # 3
print(next(v)) # 4
print(next(v)) # 5
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode
def gen():
    for x in [0, 1, 2]:
        yield from x

v = gen()

print(next(v))
# TypeError: 'int' object is not iterable
Enter fullscreen mode Exit fullscreen mode

<yield & yield from>:

def gen():
    for x in [[0, 1, 2], [3, 4, 5]]:
        yield x
        yield from x

v = gen()

print(next(v)) # [0, 1, 2]
print(next(v)) # 0
print(next(v)) # 1
print(next(v)) # 2
print(next(v)) # [3, 4, 5]
print(next(v)) # 3
print(next(v)) # 4
print(next(v)) # 5
print(next(v)) # StopIteration:
Enter fullscreen mode Exit fullscreen mode

Top comments (0)