python -- 生成器 generator

生成器 generator

列表生成式

列表生成式可以使代码更简洁。在Python中,一遍循环一边计算的机制,称为生成器:generator。

# a变量是一个列表
a = [1, 2, 3]
# b变量使用列表生成器生成了一个列表
# 列表生成器的好处,可以动态地生成列表
b = [i*2 for i in range(5)]

'''
如果a也要b的结果就需要这样写,代码变长了。
a = []
for i in range(5):
    a.append(i*2)
'''

# <class 'list'> [1, 2, 3]
print(type(a), a)
# <class 'list'> [0, 2, 4, 6, 8]
print(type(b), b)
# a变量是一个列表
a = [1, 2, 3]
# b变量使用列表生成器生成了一个列表
# 列表生成器的好处,可以动态地生成列表
b = [i*2 for i in range(5)]

'''
如果a也要b的结果就需要这样写,代码变长了。
a = []
for i in range(5):
    a.append(i*2)
'''

# <class 'list'> [1, 2, 3]
print(type(a), a)
# <class 'list'> [0, 2, 4, 6, 8]
print(type(b), b)

# a变量是一个列表
a = [1, 2, 3]
# b变量就使用列表生成器
# 列表生成器的好处是动态生成列表
b = [i*2 for i in range(500)]
# c变成了一个生成器,返回一个内存地址,c不能像list列表一样去切片。因为数据并非生成。
c = (i*2 for i in range(500))

# <class 'generator'> <generator object <genexpr> at 0x00000000022A2E60>
print(type(c), c)
# 返回一个内存分配对象
print(id(a))
print(id(b))
print(id(c))
# a变量是一个列表
a = [1, 2, 3]
# b变量就使用列表生成器
# 列表生成器的好处是动态生成列表
b = [i*2 for i in range(500)]
# c变成了一个生成器,返回一个内存地址,c不能像list列表一样去切片。因为数据并非生成。
c = (i*2 for i in range(500))

# <class 'generator'> <generator object <genexpr> at 0x00000000022A2E60>
print(type(c), c)
# 返回一个内存分配对象
print(id(a))
print(id(b))
print(id(c))

生成器只有在调用时,才会生出相应的数据。

生成器只有next方法,且用循环的方式调用

a = (i*2 for i in range(500))

# 0
print(a.__next__())
# <method-wrapper '__next__' of generator object at 0x0000000001F22E60>
print(a.__next__)

for i in range(5):
    print(a.__next__())
    
'''
以下是程序的效果: 
可以看到最后的for循环是接着之前的a.__next__()执行。
0
<method-wrapper '__next__' of generator object at 0x00000000007D2E60>
2
4
6
8
10
'''
a = (i*2 for i in range(500))

# 0
print(a.__next__())
# <method-wrapper '__next__' of generator object at 0x0000000001F22E60>
print(a.__next__)

for i in range(5):
    print(a.__next__())
    
'''
以下是程序的效果: 
可以看到最后的for循环是接着之前的a.__next__()执行。
0
<method-wrapper '__next__' of generator object at 0x00000000007D2E60>
2
4
6
8
10
'''

使用函数生成一个生成器 Step 1

著名的菲波拉契数列,除第一个和第二个数外,任意一个数都由前两个数相加而得。

1, 1, 2, 3, 5, 8, 13, 21, 34, ... ...

# 定义一个fib的函数
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n += 1

fib(10)

# a, b = b, a + b解释
# 相当于:
# t = (b, a+b) t是一个tuple元组
# a = t[0]
# b = t[1]

'''
以下是程序的结果:
1
1
2
3
5
8
13
21
34
55
'''
# 定义一个fib的函数
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n += 1

fib(10)

# a, b = b, a + b解释
# 相当于:
# t = (b, a+b) t是一个tuple元组
# a = t[0]
# b = t[1]

'''
以下是程序的结果:
1
1
2
3
5
8
13
21
34
55
'''

使用函数生成一个生成器 Step 2 -the End

# 定义一个fib的函数
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1

# 此时fib()就是一个生成器

# <generator object fib at 0x0000000002822E60>
print(fib(10))
# <generator object fib at 0x0000000002822E60>
print(fib(20))

'''
以下是程序的结果:
<generator object fib at 0x0000000001F12E60>
<generator object fib at 0x0000000002822E60>
'''
# 定义一个fib的函数
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1

# 此时fib()就是一个生成器

# <generator object fib at 0x0000000002822E60>
print(fib(10))
# <generator object fib at 0x0000000002822E60>
print(fib(20))

'''
以下是程序的结果:
<generator object fib at 0x0000000001F12E60>
<generator object fib at 0x0000000002822E60>
'''

函数式生成器的用法

使用了函数生成器后,函数可以中断,再执行别的代码,然后再回到函数中断前的地方继续执行。

# 定义一个fib的函数生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1
    return "Done"

f = fib(6)

# 使用了函数生成器后,函数可以中断,再执行别的代码,然后再回到函数中断前的地方继续执行
print('----------')  # black
print(f.__next__())
print(f.__next__())
print('\033[31;1m----------\033[0m')  # red
print(f.__next__())
print(f.__next__())
print('\033[32;1m----------\033[0m')  # green
print(f.__next__())
print(f.__next__())
print('\033[33;1m----------\033[0m')  #yellow
# 定义一个fib的函数生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1
    return "Done"

f = fib(6)

# 使用了函数生成器后,函数可以中断,再执行别的代码,然后再回到函数中断前的地方继续执行
print('----------')  # black
print(f.__next__())
print(f.__next__())
print('\033[31;1m----------\033[0m')  # red
print(f.__next__())
print(f.__next__())
print('\033[32;1m----------\033[0m')  # green
print(f.__next__())
print(f.__next__())
print('\033[33;1m----------\033[0m')  #yellow

使用函数生成一个生成器,函数中return的作用。生成器异常时,打印的消息。

# 定义一个fib的函数生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1
    return "Done"

f = fib(4)

print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())

'''
以下是程序的效果:
StopIteration: Done
1
1
2
3
'''
# 定义一个fib的函数生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1
    return "Done"

f = fib(4)

print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())

'''
以下是程序的效果:
StopIteration: Done
1
1
2
3
'''

避免生成器溢出后的报错处理,抓报错信息while [ try, except ]。

# 定义一个fib的函数生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1
    return "Done"

f = fib(10)

while True:
    try:
        x = next(f)
        print(x)
    except StopIteration as e:
        print(e.value)  # 同 print(e)
        break

# 定义一个fib的函数生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # print(b)
        yield b  # 这样就是一个生成器的写法
        a, b = b, a + b
        n += 1
    return "Done"

f = fib(10)

while True:
    try:
        x = next(f)
        print(x)
    except StopIteration as e:
        print(e.value)  # 同 print(e)
        break

用yield模拟单线程并发效果。又称,协程,比线程要小的单位,协程寄生在线程里。

Part 1 - 2

import time

def consumer(name):
    print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
    while True:
        # yield是保存当前位置,并返回出生成器,便于回来
        baozi = yield
        print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))

baozi_1 = '韭菜馅'

# 这句话不会有任何反应
c = consumer('Gao')
# 开始执行第一句print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 执行第二句的print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 这里用c.send('str')把baozi_1的内容传给了第二句的print()
# 包子韭菜馅来了,被Gao吃了!
# send是将变量传给yield的位置,以便程序生成器继续
c.send(baozi_1)


def producer(name):
    pass


'''
yield保存当前状态并返回
__next__()只是唤醒生成器
send('str')是唤醒生成器,并将str传给yield
'''
import time

def consumer(name):
    print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
    while True:
        # yield是保存当前位置,并返回出生成器,便于回来
        baozi = yield
        print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))

baozi_1 = '韭菜馅'

# 这句话不会有任何反应
c = consumer('Gao')
# 开始执行第一句print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 执行第二句的print()的内容
c.__next__()
# 执行第二句的print()的内容
c.__next__()
print('--------------------')
# 这里用c.send('str')把baozi_1的内容传给了第二句的print()
# 包子韭菜馅来了,被Gao吃了!
# send是将变量传给yield的位置,以便程序生成器继续
c.send(baozi_1)


def producer(name):
    pass


'''
yield保存当前状态并返回
__next__()只是唤醒生成器
send('str')是唤醒生成器,并将str传给yield
'''

Part 2 - 2

import time

def consumer(name):
    print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
    while True:
        # yield是保存当前位置,并返回出生成器,便于回来
        baozi = yield
        print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))


def producer(name):
    # 这一步是将consumer函数变成生成器,其他什么都没做。
    c1 = consumer(name)
    name_2 = "{0}的兄弟".format(name)
    c2 = consumer(name_2)
    
    # 这一步是让consumer这个生成器,走到yield,并返回yield的位置。因为走到yield这里,所以打印了consumer生成器的第一个print()语句
    c1.__next__()
    c2.__next__()

    print("\033[34;1m~~~开始吃包子了~~~\033[0m")
    for i in range(5):
        time.sleep(2)
        print("~~~做好一个包子了~~~")
        # 这一步是接受了i的值,并且继续上次yield的位置,继续下去,继续while里面的print()语句。
        c1.send(i)
        c2.send(i)

producer('Gao')
import time

def consumer(name):
    print("\033[32;1m{}准备吃包子啦!\033[0m".format(name))
    while True:
        # yield是保存当前位置,并返回出生成器,便于回来
        baozi = yield
        print("\033[33;1m包子{0}来了,被{1}吃了!\033[0m".format(baozi, name))


def producer(name):
    # 这一步是将consumer函数变成生成器,其他什么都没做。
    c1 = consumer(name)
    name_2 = "{0}的兄弟".format(name)
    c2 = consumer(name_2)
    
    # 这一步是让consumer这个生成器,走到yield,并返回yield的位置。因为走到yield这里,所以打印了consumer生成器的第一个print()语句
    c1.__next__()
    c2.__next__()

    print("\033[34;1m~~~开始吃包子了~~~\033[0m")
    for i in range(5):
        time.sleep(2)
        print("~~~做好一个包子了~~~")
        # 这一步是接受了i的值,并且继续上次yield的位置,继续下去,继续while里面的print()语句。
        c1.send(i)
        c2.send(i)

producer('Gao')

写在后面
  • 生成器可以用来省内存。
  • 生成器只有一个方法,next()。
  • 生成器的__next__()只能往后走,不能回退,只记录当前数值。
  • 生成器只有在调用__next__()时,超时范围,就会返回return值,异常。for,while循环不会。
  • 函数变成生成器,只要加入yield即可。
  • 生成器可以模拟出单线程并发的效果。
  • g.__next__()去激活yield,g.send('str')赋值给yield。