2024年3月6日更新了内容
2022年9月5日更新了内容
2022年8月27日更新了内容

在这里插入图片描述

一、生成器的由来

生成器继承于迭代器,迭代器继承于可迭代对象,他是一个特殊的迭代器,拥有迭代器的所有特性
在这里插入图片描述
列表和列表生成器都无法解决内存受限的问题,列表中的所有数据都保存在内存中,以至于列表中的元素的太多了,当列表中的元素超过超过几十万甚至几百万,大量数据占用电脑内存,从而导致内存溢出,不利于我们后面的编程,所以要引用生成器

二、生成器定义

生成器是python中的一个对象(按照某种规律,来生成元素的对象),生成器不是列表,保存了产生元素的算法,同时会记录游标的位置(现在拿到第几个元素了),为了下次继续拿数据,而不是从头开始拿数据。可以通过一直调用next()方法获取值,这个对象不保存数据,每次调用会返回一个值,即做到了列表的好处,又不占用空间。

1、生成器的优势

在程序中存放百万级别的数据时,能够显著减少内存开销

2、为什么生成器能够减少内存开销?

生成器内部实际不存储数据,只保存数据生成的计算规则,几乎没有什么内存开销。

三、创建生成器

1、函数中使用了关键字yield,那这个函数就不再是一个普通的函数了,而是一个生成器函数
2、生成器函数调用时,不会执行函数内部的代码,会直接返回一个生成器对象
3、关键字yield的作用
是用来生成数据
yield data

第一种方法:通过类似于列表生成式来创建(将列表换为元组)

在这里插入图片描述
generator:表示生成器

第二种方法:通过函数创建生成器(yield)

1、例如创建有限的斐波拉且数列

在这里插入图片描述
yield一般用于创建生成器,作用:返回后面变量(b)给生成器,而不是返回给函数的,b就是斐波拉且数列中的一个元素
在这里插入图片描述
只要在函数中有yield关键字,那么当前这个函数是属于生成器中保存的算法,算法实现的功能就是生成b
在这里插入图片描述
不会打印done

2、例如创建无限的斐波拉且数列,不能用for循环

在这里插入图片描述
注意:yield:是没有返回值的,如果赋值的话会打印None
在这里插入图片描述
输出结果为:
在这里插入图片描述

四、遍历生成器中的元素

第一种方法:next(生成器对象)

通过next(g),每调用一次next(),就会拿掉一个值(表达式中的第一个值),当已经遍历到生成器的结尾,会抛一个异常StopIteration。第4次调用next()的时候,生成器中已经没有数据了,继续调用会报错
在这里插入图片描述

第二种方法:for循环

通过for循环遍历,for循环不会抛出异常
在这里插入图片描述
for循环的打印结果为:4,5,6,7,因为调用了一次next(g),游标已经往下走了,

第三种方法:生成器对象.next()

object内置的__next__,当已经遍历到生成器的结尾,会抛一个异常StopIteration
在这里插入图片描述

第四种方法:list(g)

object内置的__next__,当已经遍历到生成器的结尾,会抛一个异常StopIteration

def work():
    for i in range(10):
        yield "生成器的数据:{}".format(i)

g=work()
print(list(g))

执行结果:[‘生成器的数据:0’, ‘生成器的数据:1’, ‘生成器的数据:2’, ‘生成器的数据:3’, ‘生成器的数据:4’, ‘生成器的数据:5’, ‘生成器的数据:6’, ‘生成器的数据:7’, ‘生成器的数据:8’, ‘生成器的数据:9’]

五、生成器中的方法

生成器比迭代器多了3个方法

1、close():

关闭生成器

g=(i for i in range(5))
print(g.__next__())
print(g.__next__())
print(g.__next__())
g.close()
print(g.__next__())

生成3个元素后,生成器将不在生成对象了
报:StopIteration

2、send()重点掌握

send():和next一样可以用来生成数据,可以往生成器内部传递数据(可以和生成器内部进行交互)

使用生成器时的注意:
1、不管使用next方法还是send方法,每次执行到yield时,都会暂停,并且将yield后面的值返回出来
2、send使用时,生成器内部必须处于yield暂停的位置(使用send之前,生成器至少是通过next去生成一次数据)

下面案例1报错

def demo():
    for i in range(10):
        yield i

g=demo()
print('send生成的数据:',g.send(6))

print(‘send生成的数据:’,g.send(6))
TypeError: can’t send non-None value to a just-started generator

原因是,使用send之前没有通过next去生成一次数据

案例2

特别重要:send方法传入的数据到yield前面res
当执行生成器的时候,首先生成0,将0输出,并且停止yield这行代码中;
到执行send方法的时候,从上一个生成数据的地方,继续往下执行,
如果我有传递参数,会传递给上一次暂停的地方yield前面的变量;
也就是说res就是send方法传递过来的数据。

def demo():
    for i in range(10):
        res = yield i
        print('send传入的数据,', res)


g = demo()
print('next生成的数据:', next(g))

print('send生成的数据:', g.send(6))
print('send生成的数据:', g.send(7))
print('send生成的数据:', g.send(8))

next生成的数据: 0
send传入的数据, 6
send生成的数据: 1
send传入的数据, 7
send生成的数据: 2
send传入的数据, 8
send生成的数据: 3

案例3:定义一个可以使用send传入域名,自动生成一个在前面加上http://,在后面加上路径/user/login的url地址,生成器最多可以生成5个url,生成5条数据之后再去生成,则报错StopIteration

def work():
    host=yield          #todo 2、代码暂停到这里
    for i in range(5):
        host=yield "https://{}/user/login".format(host)

g=work()
next(g)                 #todo 1、启动生成器

print('send方法生成的数据:',g.send("www.baidu.com"))       #todo 3、send方法传值,会把值传到host     4、执行for循环把host传到yield中,并且返回
#todo 5、到行代码就停下来了
print('send方法生成的数据:',g.send("www.baidu1.com"))
print('send方法生成的数据:',g.send("www.baidu2.com"))
print('send方法生成的数据:',g.send("www.baidu3.com"))
print('send方法生成的数据:',g.send("www.baidu4.com"))

执行结果:
send方法生成的数据: https://www.baidu.com/user/login
send方法生成的数据: https://www.baidu1.com/user/login
send方法生成的数据: https://www.baidu2.com/user/login
send方法生成的数据: https://www.baidu3.com/user/login
send方法生成的数据: https://www.baidu4.com/user/login

案例4:实现一个可以根据姓名、电话、年领的生成器

可以根据send方法传入的值,来生成不同的数据
1 名字
2 电话
3 年领

from faker import Faker
fk=Faker(locale='zh-CN')


def work():
    number=yield
    while True:
        if number==1:
            number=yield fk.name()
        elif number==2:
            number=yield fk.phone_number()
        elif number==3:
            number=yield fk.address()
        else:
            yield {"name":fk.name(),"phone":fk.phone_number(),"address":fk.address()}

g=work()
next(g)
print('生成的内容:',g.send(1))
print('生成的内容:',g.send(2))
print('生成的内容:',g.send(3))

执行结果
生成的内容: 胡利
生成的内容: 18913619274
生成的内容: 辽宁省小红县南长段街U座 687028

执行逻辑

当number=2时,本轮循环已经结束了,开始下一轮循环,当开始下一轮循环时,number=2

在这里插入图片描述

3、throw()

在生成器中抛出异常,效果等同于raise

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐