Iterator in Python (7)



This content originally appeared on DEV Community and was authored by Super Kai (Kazuya Ito)

Buy Me a Coffee☕

*Memo:

gi_yieldfrom can return an iterator if the generator is resumed at yield from otherwise it returns None as shown below:

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

gen = func()

while True:
    try:
        print(gen.gi_yieldfrom, next(gen.gi_yieldfrom))
    except:
        print(gen.gi_yieldfrom, next(gen))
# None 0
# <list_iterator object at 0x000001C66EBFE410> 1
# <list_iterator object at 0x000001C66EBFE410> 2
# None 3
# <list_iterator object at 0x000001C66EBFE410> 4
# <list_iterator object at 0x000001C66EBFE410> 5

gi_running can check if the generator is currently running as shown below:

<yield>:

def func():
    print(gen.gi_running, "func") # True func
    yield 0
    print(gen.gi_running, "func") # True func
    yield 1
    print(gen.gi_running, "func") # True func
    yield 2
    print(gen.gi_running, "func") # True func

gen = func()

print(gen.gi_running) # False
print(next(gen))      # 0
print(gen.gi_running) # False
print(next(gen))      # 1
print(gen.gi_running) # False
print(next(gen))      # 2
print(gen.gi_running) # False
print(next(gen))      # StopIteration:

<yield from>:

def func():
    print(gen.gi_running, "func") # True func
    yield from [0, 1]
    print(gen.gi_running, "func") # True func
    yield from [2, 3]
    print(gen.gi_running, "func") # True func
    yield from [4, 5]
    print(gen.gi_running, "func") # True func

gen = func()

print(gen.gi_running) # False
print(next(gen))      # 0
print(gen.gi_running) # False
print(next(gen))      # 1
print(gen.gi_running) # False
print(next(gen))      # 2
print(gen.gi_running) # False
print(next(gen))      # 3
print(gen.gi_running) # False
print(next(gen))      # 4
print(gen.gi_running) # False
print(next(gen))      # 5
print(gen.gi_running) # False
print(next(gen))      # StopIteration:

gi_suspended can check if the generator is currently suspended(paused) as shown below:

<yield>:

def func():
    print(gen.gi_suspended, "func") # False func
    yield 0
    print(gen.gi_suspended, "func") # False func
    yield 1
    print(gen.gi_suspended, "func") # False func
    yield 2
    print(gen.gi_suspended, "func") # False func

gen = func()

print(gen.gi_suspended) # False
print(next(gen))        # 0
print(gen.gi_suspended) # True
print(next(gen))        # 1
print(gen.gi_suspended) # True
print(next(gen))        # 2
print(gen.gi_suspended) # True
print(next(gen))        # StopIteration:

<yield from>:

def func():
    print(gen.gi_suspended, "func") # False func
    yield from [0, 1]
    print(gen.gi_suspended, "func") # False func
    yield from [2, 3]
    print(gen.gi_suspended, "func") # False func
    yield from [4, 5]
    print(gen.gi_suspended, "func") # False func

gen = func()

print(gen.gi_suspended) # False
print(next(gen))        # 0
print(gen.gi_suspended) # True
print(next(gen))        # 1
print(gen.gi_suspended) # True
print(next(gen))        # 2
print(gen.gi_suspended) # True
print(next(gen))        # 3
print(gen.gi_suspended) # True
print(next(gen))        # 4
print(gen.gi_suspended) # True
print(next(gen))        # 5
print(gen.gi_suspended) # True
print(next(gen))        # StopIteration:

A yield statement can be assigned to a variable to be used with or without send() as shown below:

*Memo:

  • send() starts or resumes the generator, sends a value into the variable assigned a yield statement only when resuming the generator, executes a yield statement to return a value and pauses the generator or raises StopIteration if the generator is terminated:
    • The 1st argument is value(Required-Type:Any):
      • It must be None when starting the generator.
      • Don’t use value=.
  • The variable assigned a yield statement has None by default.
  • yield from only with a generator(yield from generator) works with send() properly.

<yield without send()>:

def func():
    v1 = yield 0
    print(v1, 'func')
    v2 = yield 1
    print(v2, 'func')
    v3 = yield 2
    print(v3, 'func')

gen = func()

print(next(gen)) # 0
print(next(gen)) # None func
                 # 1
print(next(gen)) # None func
                 # 2
print(next(gen)) # StopIteration:

<yield with send()>:

def func():
    v1 = yield 0
    print(v1, 'func')
    v2 = yield 1
    print(v2, 'func')
    v3 = yield 2
    print(v3, 'func')

gen = func()

print(gen.send(None)) # 0
print(gen.send('A'))  # A func
                      # 1
print(gen.send('B'))  # B func
                      # 2
print(gen.send('C'))  # C func
print(gen.send('D'))  # StopIteration:
def func():
    v1 = yield 0
    print(v1, 'func')
    v2 = yield 1
    print(v2, 'func')
    v3 = yield 2
    print(v3, 'func')

gen = func()

print(gen.send('A'))
# TypeError: can't send non-None value to a just-started generator

<yield from without send()>:

def func():
    v1 = yield from [0, 1]
    print(v1, 'func')
    v2 = yield from [2, 3]
    print(v2, 'func')
    v3 = yield from [4, 5]
    print(v3, 'func')

gen = func()

print(next(gen)) # 0
print(next(gen)) # 1
                 # None func
print(next(gen)) # 2
print(next(gen)) # 3
                 # None func
print(next(gen)) # 4
print(next(gen)) # 5
                 # None func
print(next(gen)) # StopIteration:

<yield from with send()>:

def sub_func():
    v1 = yield 0
    print(v1, 'sub_func')
    v2 = yield 1
    print(v2, 'sub_func')
    v3 = yield 2
    print(v3, 'sub_func')

def func():
    v = yield from sub_func()
    print(v, 'func')

gen = func()

print(gen.send(None)) # 0
print(gen.send('A'))  # A sub_func
                      # 1
print(gen.send('B'))  # B sub_func
                      # 2
print(gen.send('C'))  # C sub_func
                      # None func
                      # StopIteration:
def func():
    v1 = yield from [0, 1]
    print(v1, 'func')
    v2 = yield from [2, 3]
    print(v2, 'func')
    v3 = yield from [4, 5]
    print(v3, 'func')

gen = func()

print(gen.send('A'))
# TypeError: can't send non-None value to a just-started generator
def func():
    v1 = yield from [0, 1]
    print(v1, 'func')
    v2 = yield from [2, 3]
    print(v2, 'func')
    v3 = yield from [4, 5]
    print(v3, 'func')

gen = func()

print(gen.send(None)) # 0
print(gen.send('A'))
# AttributeError: 'list_iterator' object has no attribute 'send'

A yield statement cannot be assigned to a for statement as shown below:

def func():
    for v in yield [0, 1, 2]:
    # for v in yield from [0, 1, 2]:
        print(v)
# SyntaxError: invalid syntax


This content originally appeared on DEV Community and was authored by Super Kai (Kazuya Ito)