首页 > 其他分享 >py之路——day13-20230821:生成器和迭代器

py之路——day13-20230821:生成器和迭代器

时间:2023-08-21 22:57:17浏览次数:36  
标签:__ 迭代 20230821 py 生成器 print 包子

作者:zb

一、列表生成式

1、定义

用来生成列表的表达式

2、特点

可以使代码更加简洁

示例代码如下:

 1 # 普通方法定义列表
 2 a = [1, 2, 3]
 3 print(a)
 4 # 列表生成式方法定义列表
 5 b = [i*2 for i in range(10)]
 6 print(b)
 7 # 如果不用列表生成式,上述b列表定义会很麻烦
 8 c = []
 9 for i in range(10):
10     c.append(i*2)
11 print(c)
12 # 列表生成式还可以调用函数
13 d = [func(i) for i in range(10)]
14 print(d)

二、生成器

1、生成器的定义

生成器是一个对象,并不是一个实际的可迭代对象(例如元组、字典、列表等),它是一种算法,相关的元素可以通过这种算法一一计算出来,但是不循环执行算法时,生成器不会生成对应的元素。

2、生成器的特点:

⑴代码执行速度快,因为生成器只是定义了一个生成器对象,只有在被调用时才会产生实际的数据

示例代码如下:

1 # 列表生成式
2 a = [i*2 for i in range(5)]
3 print(a[1])
4 # 生成器
5 b = (i*2 for i in range(5))
6 print(b)
7 # 生成器只有被调用的时候才会生成数据,因此可以通过循环取生成器的值,生成器不可以被切片
8 for i in b:
9     print(i)

执行结果:

 1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/2、生成器.py
 2 2
 3 <generator object <genexpr> at 0x0000020927D746D0>
 4 0
 5 2
 6 4
 7 6
 8 8
 9 
10 Process finished with exit code 0

⑵生成器是一个对象,生成器和列表不同,生成器只是一个对象,并没有存储具体的数据,只有在调用生成器的时候才会返回具体的数据,而列表生成式不一样,列表生成式已经存储好了所有的数据,因此程序运行时会大量占用内存空间

示例代码如下:

1 # 生成器,只有在被调用的时候才会生成数据,否则就是一个对象而已,不占用内存空间
2 a = (i*2 for i in range(10))
3 print(a)
4 # 列表生成式,执行后就生成了所有的数据,占用内存空间
5 b = [i*2 for i in range(10)]
6 print(b)

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/2、生成器.py
2 <generator object <genexpr> at 0x00000232292946D0>
3 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
4 
5 Process finished with exit code 0

⑶如何调用生成器生成数据?

使用生成器对象的__next__()方法

示例代码如下:

1 # 生成器的__next__()方法
2 a = (i*2 for i in range(100000000))
3 print(a.__next__())
4 print(a.__next__())

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/2、生成器.py
2 0
3 2
4 
5 Process finished with exit code 0

⑷生成器运行过程中是如何节省内存空间的?原理是什么?

生成器只能使用__next__()方法生成数据,而且只会保存当前位置生成的数据,之前的数据会被丢弃掉

⑸生成器不能被切片

3、斐波那契数列

⑴简单的生成器可以使用for循环直接生成

示例代码如下:

1 b = (i*2 for i in range(5))
2 for i in b:
3     print(i)

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/2、生成器.py
2 0
3 2
4 4
5 6
6 8
7 
8 Process finished with exit code 0

⑵但是如果生成的算法比较复杂,for循环无法生成,该怎么办呢?

那就要使用函数来定义生成器的算法,例如我们先写一个生成斐波那契数列的函数

示例代码如下:

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         # 斐波那契数列每次只打印b
 5         print(b)
 6         #a, b = b, a+b,理论上a=0,b=1,此时应该是a=1,b=1+1=2,所以数列应该是1,2...而不是1,1...
 7         #所以这里的a,b=b,a+b这种赋值方法其实是t=(b,a+b),a=t[0],b=t[1],即a=1,b=1,即1,1...
 8         a, b = b, a+b
 9         n = n+1
10     print('done')
11 fib(10)

执行结果:

 1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/3、斐波那契数列.py
 2 1
 3 1
 4 2
 5 3
 6 5
 7 8
 8 13
 9 21
10 34
11 55
12 done
13 
14 Process finished with exit code 0

那么问题来了,如何将函数变成生成器呢?只需要对代码做如下改动即可

示例代码如下:

1 def fib(max):
2     n, a, b = 0, 0, 1
3     while n < max:
4         yield b
5         a, b = b, a + b
6         n = n + 1
7 f = fib(10)
8 print(f)

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/4、将函数变成生成器.py
2 <generator object fib at 0x00000241481A6030>
3 
4 Process finished with exit code 0

4、函数式生成器

⑴将函数变为生成器的好处

可以随时停止函数的调用,并穿插着做点别的事情,想继续调用函数时可以继续调用

示例代码如下:

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         yield b
 5         a, b = b, a + b
 6         n = n + 1
 7 f = fib(10)
 8 print(f)
 9 print(f.__next__())
10 print(f.__next__())
11 print(f.__next__())
12 print('===干点别的事情===')
13 print(f.__next__())
14 print(f.__next__())

执行结果:

 1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/4、将函数变成生成器.py
 2 <generator object fib at 0x000001B61F586030>
 3 1
 4 1
 5 2
 6 ===干点别的事情===
 7 3
 8 5
 9 
10 Process finished with exit code 0

⑵yield

带yield的函数,不能再称之为函数了,它已经是一个生成器了,并且可以通过return关键字返回调用过程中的异常消息(想把谁返回到外部,就把谁定义为yield,yield可以保存函数的这种中断状态)

示例代码如下:

 1 def fib(max):
 2     """
 3     定义一个斐波那契数列的生成器
 4     :param max:
 5     :return:
 6     """
 7     n,a,b = 0,0,1
 8     while n<max:
 9         yield b
10         a,b = b,a+b
11         n+=1
12     return "超过了最大循环次数"
13 f = fib(10)
14 
15 # 写一个判断异常的代码
16 while True:
17     try:
18         x = next(f)
19         print('f', x)
20     except StopIteration as e:
21         print("运行出错:", e.value)
22         break

执行结果:

 1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/5、函数生成器通过return关键字打印报错信息.py
 2 f 1
 3 f 1
 4 f 2
 5 f 3
 6 f 5
 7 f 8
 8 f 13
 9 f 21
10 f 34
11 f 55
12 运行出错: 超过了最大循环次数
13 
14 Process finished with exit code 0

⑶使用生成器yield实现单线程下的并发

使用生成器yield的特性可以实现同一时间执行多个任务,这种原理在python中叫做协程,协程是比线程更小的单位,存在于线程里,我们熟知的Nginx就是使用了协程的原理,为什么Nginx的执行效率高?原因就是单线程下的异步IO,这样导致Nginx可承载的并发量比多线程还要高好多倍,我们用一段简单的代码来实现协程,生成器的__next__()方法只是用来初始化生成器(即让生成器做好接受传值的准备),而send()方法可以给生成器传值

示例代码如下:

 1 import time
 2 
 3 def consumer(name):
 4     """
 5     消费者
 6     :param name:
 7     :return:
 8     """
 9 
10     while True:
11 
12         baozi = yield
13         print("准备吃包子啦!")
14         print("包子[%s]来了,被[%s]吃了!" % (baozi, name))
15 
16 def producer():
17     """
18     生产者
19     :return:
20     """
21     c1 = consumer('A')  # 调用生成器
22     c2 = consumer('B')  # 调用生成器
23     c1.__next__()   # 初始化生成器
24     c2.__next__()   # 初始化生成器
25     print("老子准备开始做包子啦!")
26     for i in range(1,11):
27         print("做了一个包子,分两半!")
28         time.sleep(1)
29         c1.send(i)
30         c2.send(i)
31 
32 producer()

执行结果:

 1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/6、通过yield(生成器)实现单线程下的并发(即协程).py
 2 老子准备开始做包子啦!
 3 做了一个包子,分两半!
 4 准备吃包子啦!
 5 包子[1]来了,被[A]吃了!
 6 准备吃包子啦!
 7 包子[1]来了,被[B]吃了!
 8 做了一个包子,分两半!
 9 准备吃包子啦!
10 包子[2]来了,被[A]吃了!
11 准备吃包子啦!
12 包子[2]来了,被[B]吃了!
13 做了一个包子,分两半!
14 准备吃包子啦!
15 包子[3]来了,被[A]吃了!
16 准备吃包子啦!
17 包子[3]来了,被[B]吃了!
18 做了一个包子,分两半!
19 准备吃包子啦!
20 包子[4]来了,被[A]吃了!
21 准备吃包子啦!
22 包子[4]来了,被[B]吃了!
23 做了一个包子,分两半!
24 准备吃包子啦!
25 包子[5]来了,被[A]吃了!
26 准备吃包子啦!
27 包子[5]来了,被[B]吃了!
28 做了一个包子,分两半!
29 准备吃包子啦!
30 包子[6]来了,被[A]吃了!
31 准备吃包子啦!
32 包子[6]来了,被[B]吃了!
33 做了一个包子,分两半!
34 准备吃包子啦!
35 包子[7]来了,被[A]吃了!
36 准备吃包子啦!
37 包子[7]来了,被[B]吃了!
38 做了一个包子,分两半!
39 准备吃包子啦!
40 包子[8]来了,被[A]吃了!
41 准备吃包子啦!
42 包子[8]来了,被[B]吃了!
43 做了一个包子,分两半!
44 准备吃包子啦!
45 包子[9]来了,被[A]吃了!
46 准备吃包子啦!
47 包子[9]来了,被[B]吃了!
48 做了一个包子,分两半!
49 准备吃包子啦!
50 包子[10]来了,被[A]吃了!
51 准备吃包子啦!
52 包子[10]来了,被[B]吃了!
53 
54 Process finished with exit code 0

三、迭代器

1、可迭代对象

⑴定义:

可直接作用于for循环的数据类型有以下两类:

a、集合类数据类型:list、tuple、dict、set、str

b、genator生成器

这些可直接作用于for循环的对象统称为可迭代对象,即可循环对象Iterable

⑵如何判断一个对象是否是可迭代对象?使用isinstance函数

示例代码如下:

1 from collections.abc import Iterable
2 
3 print(isinstance([], Iterable))
4 print(isinstance({}, Iterable))
5 print(isinstance('abc', Iterable))
6 print(isinstance(100, Iterable))

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/迭代器.py
2 True
3 True
4 True
5 False
6 
7 Process finished with exit code 0

2、迭代器

⑴定义:可以被next()函数调用,可以使用__next__()方法并不断返回下一个值的对象称为迭代器

⑵如何判断一个对象是否是迭代器?同样使用isinstance()函数

示例代码如下:

1 from collections.abc import Iterator
2 
3 print(isinstance((x for x in range(10)), Iterator))
4 print(isinstance([], Iterator))
5 print(isinstance({}, Iterator))

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/迭代器.py
2 True
3 False
4 False
5 
6 Process finished with exit code 0

生成器一定是迭代器,但是迭代器不一定是生成器,因为迭代器必须拥有__next__()方法,但是列表等可迭代对象没有该方法,那么如何将可迭代对象转换为迭代器呢?使用iter()函数

示例代码如下:

1 from collections.abc import Iterator
2 
3 print(isinstance(iter([]), Iterator))

执行结果:

1 D:\oldboy_py\venv\Scripts\python.exe D:/oldboy_py/day3-20230524迭代器与生成器/迭代器.py
2 True
3 
4 Process finished with exit code 0

⑶为什么list、dict、set等不能是迭代器呢?

因为迭代器的计算是惰性的,即需要返回下一个数据时,它才会计算,并且没有明确的开始和结束,而list等必须有明确的开始和结束,因此这种存在无限大可能得数据流才能是迭代器,range()函数其实就是迭代器

⑷如何查看一个对象可以使用的所有方法?使用dir()函数

示例代码如下:

1 >>> dir([])
2 ['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
3 >>>

发现没有__next__()方法,因此不是迭代器

 

标签:__,迭代,20230821,py,生成器,print,包子
From: https://www.cnblogs.com/qq2751044056/p/17647300.html

相关文章

  • 搭建Python⾃带静态Web服务器
    说明Python自带的静态Web服务器可以通过使用http.server模块来实现。步骤打开命令行终端,进入要作为静态文件根目录的文件夹。运行以下命令启动静态Web服务器:Python2.x版本:python-mSimpleHTTPServer<port>Python3.x版本:python-mhttp.server<port> <port>是可选参......
  • Python中的 if __name__ == “__main__“
    ✅作者简介:热爱科研的算法开发者,Python、Matlab项目可交流、沟通、学习。......
  • python设置全局热键
    需要使用第三方库,代码如下:importtkinterastkimportkeyboarddefoutput(event):#print("Fromglobalkeystroke")print(event)root=tk.Tk()root.withdraw()keyboard.add_hotkey('ctrl+a',output,args=('Fromglobalkeystroke'......
  • 排查Python卡慢神器
    如果遇到Python正在运行中的进程卡住,找不到原因。可以试试以下工具方法,对于python就像jstack对于java一样。法一使用pystack-debugger安装方式如下:yuminstallgdbpipinstallpystack-debugger1.查看线程IDpsajx|grepgunicorn2.查看堆栈信息,确认有问题代码pystac......
  • 20230821比赛
    20230821比赛T1【佛山市选2013】树环转换GMOJ3230Description给定一棵N个节点的树,去掉这棵树的一条边需要消耗值1,为这个图的两个点加上一条边也需要消耗值1。树的节点编号从1开始。在这个问题中,你需要使用最小的消耗值(加边和删边操作)将这棵树转化为环,不允许有重边。环的定......
  • PYYZ8.21赛总
    T1T1大失误赛时边想正解边开c++14花了一个半点多,正解想到从后向前枚举,也知道查分,就卡在边向前差分边进行后缀和这一步,然后竟然开始写树状数组了(纯伞兵)然后调了40min没出,又重新敲了个暴力T2时间不多了随便写了个15pts的链,没看见打满暴力70ptsT3然后开始看了一眼,写了个自认......
  • c2工具sliver的python客户端无法修改grpc超时时间的解决办法
    业务需要,调用了很多implants来执行对应系统上的命令,但是无论怎么指定interactive.py中execute方法参数,命令执行超时时间总是30.后面通过扩展execute方法增加一个grpc超时参数后解决;具体方法如下:asyncdefexecute_ex(self,exe:str,args:List[str],output:bool,tim......
  • 轻松掌握图像处理技能,Python OpenCV库带你飞
    所有代码需要在安装了OpenCV库的Python环境中运行。1图像基础操作importcv2ascvimg=cv.imread("./img/run.png",flags=cv.IMREAD_COLOR)print(img)[[[255255255][255255255][255255255]...[255255255][255255255][255255255]]......
  • Python学习日记 2023年8月21日
    importrequestsimportosimportrefromtimeimportsleepfrombs4importBeautifulSoupurl='http://www.netbian.com/mei/'headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,like......
  • windows 桌面GUI自动化-2. pywinauto 启动指定应用程序
    前言pywinauto可以启动电脑自带的应用程序,也可以启动直接安装的应用启动电脑自带的应用程序上一篇环境准备,可以启动记事本了frompywinauto.applicationimportApplication#启动记事本app=Application(backend="uia").start("notepad.exe")通过start()方法指定exe......