python中的强大工具——生成器

python中的强大工具——生成器

一、迭代器回顾

1、什么是迭代器

​ 在上一篇文章中,我们讲到了迭代器,所谓迭代器就是访问集合元素的一种方式,迭代器是一个可以记住遍历位置的一个对象,它可以将集合集合中的元素挨个访问,直到访问完所有元素。

2、迭代器的原理

​ 在上一篇文章中,我们自定义了一个对象,让该对象那个成为了可迭代对象和迭代器,并且可以通过for循环迭代出来元素。那么迭代器他最重要的就是next方法,所有利用迭代器,可以在每次迭代获取数据(通过next获取)时,按照特定的规律进行生成(比如说:我们最后做的使用迭代器算斐波那契数列),但是我们在实现迭代器的时候,关于当前迭代的状态(或者说位置)需要我们自己手动去记录(定义了一个变量),进而才能根据当前的状态去生成下一个数据。那么也就是说我们需要通过记录当前的位置,并且配合next方法才能生成下一个数据,但是这种方法有一个缺点:需要我们提前将数据创建好(比如说算斐波那契数列时定义的两个变量a,b)。

3、迭代器的应用

​ 在python中不仅for……in循环遍历的是迭代器,就连一些容器类型的转换等也需要使用迭代器

比如 :list 类型 ==> tuple类型,它也是利用迭代器将一个个元素通过next方法迭代出来,可能是装到另外一个容器中

二、生成器

1、什么是生成器

在python中一边循环一边计算的机制,我们称之为生成器(generator),它是特殊的迭代器。为什么说它是特殊的迭代器,因为它也是将一个个的数据迭代出来,不过不是使用return,而是使用 yield 关键字往出返,并且这个 yield 还会自动记录位置,或者说是挂起,当然这只是生成器的一种标志,它还有其他的方式生成。

2、为什么要有生成器

​ 比如说你要使用一些数据,那么肯定是首先想到使用列表,或者列表推导式,那么你将该列表提前定义好放在程序中,它每次执行的时候就都要去消耗大量的内存,当你好访问全部元素那还好,那如果你仅仅只访问前几个元素,那么其他的元素不是都在占用大量的资源吗?

​ 如果列表中的元素按某种算法算出来,那我么就可以在循环的过程中不断推算出后续的元素,这样我们就必须要创建一个完整的列表来占用资源了,也就节省了大量的内存空间。

​ 那么就是一句话,如果想要得到庞大的数据,又想让其占用的内存较少,那么就直接上生成器吧!

3、如何创建生成器

方法一:

​ 要想创建生成器,那么就先学会使用列表推导式,并且将列表推导式的 [] 变为 () 即可

# 列表推导式算1-5的平方
my_list = [i ** 2 for i in range(1, 6)]
print(my_list)

# 生成器1-5的平方
my_generator = (i ** 2 for i in range(1, 6))
print(my_generator)
# 使用for循环 迭代 生成器,生成器自动计算后续的值,并返回
for item in my_generator:
    print(item)

输出结果:

# 列表推导式输出的结果
[1, 4, 9, 16, 25]
# 生成器输出结果,发现返回了一个generator对象,即生成器对象
<generator object <genexpr> at 0x7f396ffcaa50>
1
4
9
16
25
方法二:

​ 创建生成器只需要在一个函数中使用 yield 关键字,那么该函数就不是普通的函数了,而是一个生成器,那么也就是说 yield 是生成器的标志,并且它就是一个生成器的模板。

# 比如要使用生成器算1-10000之内的完数
# 所谓完数就是水仙花数字,个十百位数每一位的 三次方之和 == 自身,那么就是完数
def perfect_num(start,end):
    while start < end:
        # 分别拿到个十百千上的数字
        ge = start % 10
        shi = (start % 100) // 10
        bai = (start % 1000) // 100
        qian = start // 1000
        # 因为不包含end所有必须小于end
        if start < end:
            # 个十百位数每一位的 三次方之和 == 自身
            if start == ge ** 3 + shi ** 3 + bai ** 3 + qian ** 3:
                # 返回strat,并挂起,或者说记住此时的位置,下一次再从yield开始执行
                yield start
            # 不管条件是否成立都要算下一位的 
            start += 1

if __name__ == "__main__":
    # 实例化生成器对象
    rust = perfect_num(1,10000)
    # 输出查看是否是一个对象
    print(rust)
    # 循环遍历生成器对象
    for item in rust:
        print(item)

4、yield关键字

1、本质

​ 将该函数标记为生成器对象,在调用含有 yield 关键字的函数时,目标生成一个生成器对象,对这个生成器对象进行迭代时,每次迭代都只执行到 yield 关键字这块,而 yield 后边的数据 是本次迭代的产物 数据,只要不进行下一次的迭代,代码流程一直监听在 yield 这块。

2、作用

​ (1)保存当前的运行状态,或者说记住当前运行位置,也就是将生成器挂起,等待下一次调用后继续往后执行

​ (2)yield 关键字后边的 表达式作为 值返回

​ 注意:python3中的生成器可以使用return返回最终运行的返回值,而在python2中生成器里边绝对不允许出现ruturn的返回值(也就是说python2中可以使用return,但是return后面绝对不能有数据表达式),那么生成器中如何使用return呢?

3、生成器中使用return
# 创建一个生成器函数
def work(start,end):
    while start < end:
        print("----------1------------")
        yield start
        print("----------2------------")
        start += 1
        print("----------3------------")
    else:
        return "超出生成器的迭代范围"
if __name__ == "__main":
    # 使用异常捕获,捕获异常
	try:
        # 实例化生成器对象
    	w = work(1,6)
        # 使用for循环遍历
    	for _ in range(60):
            # 输出下一个生成器对象,并使用next方法进行不断唤醒
        	print(next(w))python
   	# 当超出迭代的次数
	except StopIteration as a:
        # 打印异常的值,即return返回的值
    	print(a.value)

运行结果:

----------1------------
1
----------2------------
----------3------------
----------1------------
2
----------2------------
----------3------------
----------1------------
3
----------2------------
----------3------------
----------1------------
4
----------2------------
----------3------------
----------1------------
5
----------2------------
----------3------------
超出生成器的迭代范围

总结:

​ 那么一般情况下我们在生成器中使用ruturn的作用就是通过捕获异常,告诉客户迭代次数已经超可迭代的范围

5、唤醒生成器

(1)next( ) 函数

​ next () 函数用户一次性唤醒迭代器,或者说迭代出一个元素,也就是说只进行迭代一次,而生成器它虽说是特殊的迭代器,但它以然是迭代器

next(iterable)

(2)send( )函数

生成器对象.send(value)
特征:

​ 1、send一般不会放在第一次启动生成器,如果非要这么做,那么必须传递一个参数None

​ 2、send 里面的参数 会作为数据传递给 yield ,当作 yield 的结果,然后通过一个遍历可以接收这个结果

def work(start,end):
    while start < end:
        print("----------1------------")
        # 使用一个遍历来接收yield的结果,因为send传递了一个参数给yield
        a = yield start
        print(a)
        print("----------2------------")
        start += 1
        print("----------3------------")
    else:
        return "超出生成器的迭代范围"
try:
    w = work(1,6)
    for _ in range(60):
        print(next(w))
        # send不放在第一位,这个参数传递给yield作为yield的结果
        print(w.send("abs"))
except StopIteration as a:
    print(a.value)

输出结果:

----------1------------
1
abs
----------2------------
----------3------------
----------1------------
2
# 这是由于next造成的结果,所有yield没有值,只能返回一个None
None
----------2------------
----------3------------
----------1------------
3
abs
----------2------------
----------3------------
----------1------------
4
# 这是由于next造成的结果,所有yield没有值,只能返回一个None
None
----------2------------
----------3------------
----------1------------
5
abs
----------2------------
----------3------------
超出生成器的迭代范围

​ 3、send 的结果是下一次调用 yield 时,yeid 后面的值。也就是说 send 是第二个,是下一次给 yield 的值,因为第一次是next,因为没有给 yield 值,所有返回的是None

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御弟謌謌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值