用人话讲盘算机:Python篇!(十五)迭代器、天生器、装饰器

[复制链接]
发表于 2025-11-23 19:54:57 | 显示全部楼层 |阅读模式
一、迭代器

(1)界说

标准表明:迭代器是 Python 中实现了迭代协议的对象,即提供__iter__()和 __next__()方法,任何实现了这两个方法的对象都可以被称为迭代器。
所谓__iter__(),即返回迭代器自身
所谓__next__(),即返回容器的下一个元素。
(是不是没看懂,别急,听下面的人话怎么说)
简朴来说,你听我徐徐道来
起首,我们看python中的 for 循环:
  1. for i in 对象
复制代码
此中,对象必须是可迭代的,什么叫可迭代,就比如对象是[1,2,3],i会依次变为1,2,3,那么这个对象就是可迭代的,假如这个对象是33,i只会酿成33,这行代码还会报错,这个对象就是不可迭代的
然后,我们来明白下这行代码逻辑
假如对象是[1,2,3],那么它是可以迭代的,而这个可以迭代的对象,就叫做__iter__( ) 方法。
以是这一点,我们可以这么明白,假如没有__iter__( )方法,那么对象就不可迭代,那么步伐就会报错。
当 i 依次变为1,2,3的过程中,它能本身变,就是从1酿成2,又从2酿成3,这种可以自动下一个的过程,就叫做__next__()方法
那么迭代器我们就可以这么去明白了:
迭代器=__iter__()方法+ __next__()方法
即,一个对象,它具有__iter__()方法和__next__()方法,那么它就是迭代器
(留意,诸如__iter__()中左右横线是英文状态下的两个“_”连在一起的) 
(2)利用

我们来看一个迭代器的例子,加深明白:
  1. list = [1, 2, 3]
  2. # 用inter()方法获取迭代器对象
  3. a = iter(list)
  4. # 使用迭代器遍历元素
  5. print(next(a)) # 输出 1
  6. print(next(a)) # 输出 2
  7. print(next(a)) # 输出 3
  8. # 若再写一行print(next(a))
  9. # 则下一次调用将会抛出 StopIteration 异常,因为列表中没有更多的元素了
复制代码
表明:
我们起首界说一个列表,
然后用 iter()方法获取了可迭代对象
末了再用 next()方法举行了输出
留意:有人大概发现我们之前不绝说的都是__iter__(),而这里写的却是 iter()
缘故起因在于,这里代码中的 iter() 是一个函数,它用于调用该对象的 __iter__() 方法,进而返回一个迭代器。
那么,什么时间写 iter( )什么时间写__iter__( ) 呢?

  •  何时写 iter( ):
当你已有一个可迭代对象,并渴望得到它的迭代器时,你可以直接利用 iter() 函数。
比方,你有一个列表,想获取它的迭代器。

  • 何时写__iter__( ) :
你创建了一个类,并渴望它可以被 for 循环遍历,大概可以被 next() 函数逐个获取元素
(这个新概念“类”,不懂可以先跳过,背面的章节会具体表明,什么叫“类”)

提及利用,这里尚有个告急知识点:获取迭代器的值
在上面的例子中,我们可以看到获取迭代器的值,用的是 next(迭代器对象)
着实,我们还可以用  迭代器对象.__next__()  来获取,如:

增补::除此之外,还可以用一个报错的方法来输出

为啥我说报错呢,由于for循环时内部会报错,但是我们看不到而已。
(3)作用

我们利用时可以发现,迭代器输出时是一个一个往外蹦的,我们不消next方法,它就不会输出。
以是这里的一个告急作用,便是它只有在必要时才盘算和返回下一个数据。
这意味着它们不会一次性盘算出全部效果,而是每次调用时都产生下一个值,从而克制了不须要的盘算和内存占用。
二、天生器

(1)界说

天生器,就是一个特殊的迭代器。
特殊在哪呢?
就在于,它多了个带 yield 关键字的函数
什么是yield关键字?
你可以把它明白为函数里的return
它长什么样?它长下面如许:

此中,gen就是一个天生器。
前面谁人my_generator()函数,背面不是带了三个yield吗,以是给gen后,gen就酿成天生器了。
(2)yield关键字

我们来具体表明下,这个yield关键字:
起首,我们要知道,利用 yield 关键字的函数会酿成一个天生器,以后,它就不是平凡的函数了。(yield可以有多个)
与return差别的是: 

  • 每次调用yield时,函数会停息,并返回一个值。
即盘算机实行完第一个print(next(gen))时,函数会将当前第一个值 1 返回,然后自身停息到yield1和yield2之间。
实行第二个print(next(gen))时,函数就重新起步,然后返回当前第一个值 2 (由于它之前不是停息到yield1和yield2之间了吗,以是此时2是第一个值),然后再次停息到了yield2和yield3之间
依次类推,是谓函数会停息,并返回一个值。
以是,到此,天生器的作用也就体现出来了,即:它可以停息函数的实行,并在 必要时恢 复实行,不会一次性生玉成部的数据,而是什么时间必要,什么时间 天生,从而节省内存。 
(3)天生器的实行

说白了,就是输出值:
我们之前说了,天生器是特殊的迭代器,以是迭代器获取值的方法,天生器同样实用,即:

  • next(天生器对象)
  • 天生器对象.__next__()
  • 用for遍历


我们来看一个复杂点的例子:
  1. def fun1(L:list):    #L:list 表明 L参数预期得到一个列表类型
  2.     sum = 0
  3.     for i in L:
  4.         sum += i
  5.         yield sum
  6. l=[1,4,3]
  7. print(list(fun1(l)))    #list()将结果转换为易看懂的列表
  8. #输出[1, 5, 8]
复制代码
当l列表中第一个元素 1 到函数fun1中时,sum=1 ,返回1,然后停息
当l列表中第二个元素 4 到函数fun1中时,sum=1+4,返回5,然后停息
当l列表中第三个元素 3 到函数fun1中时,sum=5+3,返回8
而,假如把上面的yield换成return的话,终极只会输出一个1,然后之间竣事了(记得删掉list(),由于yield返回的是迭代对象,以是必要转化,而return的话则不必要)


(4)传值

前文我们说了不少获取值的方法,着实这里尚有个传入值的方法,那就是send()函数
  1. <strong>格式:</strong>生成器对象.send(value)
复制代码
此中value这个参数可有,也可无。但作用很差别。

  • 当无参数时,我们将value写作None,即 天生器对象.send(None)
此时,它和 next() 函数的功能完全雷同,如图:



  • 当有参数时,它会将停息的步伐继续实行,而 value 值会赋值给之前停息 yield 。
  1. def a():
  2.     b = yield "hello"    #此时b是第一个yield的接受者
  3.     c = yield b          #此时c是第二个yield的接受者
  4.     yield c
  5.     print(b)             #输出b的值
  6.     print(c)             #输出c的值
  7.     yield "输出完毕"
  8. d = a()
  9. print(d.send(None))
  10. print(d.send(1))    #将值1输送进去
  11. print(d.send(2))    #将值2输送进去
  12. print("输出b、c的值:")
  13. print(d.send(None))
复制代码
输出:
hello
1
2
输出b、c的值:
1
2
输出完毕
表明:
实行print(d.send(None))时,相当于next方法,即将yield返回,以是起首输出hello。(留意此时b还没有被赋值,便已经停息了。)
实行print(d.send(1))时,值1被赋值给被停息的yield,以是成了b=1
然后继续实行代码c = yield b
此时遇到yield,以是输出b的值1,然后停息。
实行print(d.send(2)),将刚才停息的yield值赋值为2,以是成了c=2,然后继续实行到第三个yield语句,输出c的值。
末了,我们又用send查验了一下b和c的值,发现确实是如许。
(5)制止运行

假如说,我们想制止该天生器,让厥后续将无法再调用 next() 函数 大概 __next__() 方法启动实行,否则抛出 StopIteration 非常
我们可以利用close()函数
如图

三、装饰器

(1)界说

标准表明:在Python中,装饰器(decorators)是一种高级功能,它答应你在不修改 原有函数或方法界说的环境下,给函数或方法添加新的功能。装饰器本质上 是一个函数,它吸取一个函数作为参数,并返回一个新的函数或修改原来的 函数。
用人话来说,比如我们有一个a函数,a函数中有 b功能代码、c功能代码、d功能代码等等。
我们不想直接去修改b功能代码,由于c功能等大概跟b有关系,要修改b的话,大概整个a函数的各板块都要大改。
以是我们可以用装饰器阐明一下,即,在别处写好一个n功能代码,然后利用装饰器,让n功能代码平替了b功能代码,如许就不消大改了。
(2)格式

格式: @须装饰的函数名
此时,我们称谁人被装饰的函数名为装饰器
除此之外,装饰器函数一样寻常必要函数闭包情势实现。
言内之意,就是装饰器必须是函数嵌套那样的。
(说一样寻常,是由于还可以利用类来实现装饰器,后续文章再具体先容)
(3)用法

比如,我们有n、c、d功能代码,我们想盘算这些代码的运行时间
假如没用装饰器,我们大概会写:
  1. import time
  2. def a1(m):
  3.     time1=time.time()
  4.     b功能代码
  5.     time2=time.time()
  6.     time0=time2-time1   
  7.     return time0
  8. def a2(m):
  9.     time1=time.time()
  10.     c功能代码
  11.     time2=time.time()
  12.     time0=time2-time1   
  13.     return time0
  14. def a3(m):
  15.     time1=time.time()
  16.     d功能代码
  17.     time2=time.time()
  18.     time0=time2-time1   
  19.     return time0
  20. print (a1(True))
  21. print (a2(True))
  22. print (a3(True))
复制代码
假如用上装饰器,就会简朴许多,
  1. import time
  2. def a(m):
  3.     def n():    #函数嵌套,即闭包
  4.         time1=time.time()    #起始那会的时间
  5.         m()
  6.         time2=time.time()    #结束那会的时间
  7.         time0=time2-time1    #计算时间差
  8.         return time0
  9.     return n
  10. @a    #启用装饰器,会将b函数平替m()
  11. def b(x):
  12.     代码
  13. print (a(True))    #输出平替后的运行b函数的时间
  14. @a     
  15. def c(x):
  16.     代码
  17. print (a(True))
  18. @a   
  19. def d(x):
  20.     代码
  21. print (a(True))
复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表