Python 生成器相关知识
-
生成器
-
生成器概念:
在Python社区,大部分人把生成器与迭代器看成是一种。生成器的本质就是迭代器。
唯一区别:生成器是我们自己用Python代码构建的数据结构。迭代器都是系统提供的,或者转化得来的。
-
获取生成器的三种方式:
-
生成器函数:
yield:只要函数中有yield,那么它就是生成器函数而不是一般的函数了。生成器函数中可以存在多个yield,一个yield对应一个next。yield不会结束函数。
return:函数中只存在一个return,遇到next即结束函数,并为执行者提供一个返回值。
def func(): a=22 yield a b=12 yield b c=a+b yield c ret=func() print(next(ret)) # yield返回值可以使用next方法逐个提取 print(next(ret)) # 每次运行只运行到当前的yield即停止 print(next(ret)) 22 12 34
-
生成器函数的应用:
其实是对数据的一种构造方法
- 吃包子例子:预订5000个包子,一次生全部生产出来占地方(内存),还有吃不完的浪费;如果使用生成器,那么就相当于生产一个吃一个,不会产生浪费不占地方,节省资源。
- 鸡蛋例子:家里需要5000个鸡蛋,有两种方法可以获取,一种是去菜场或超市直接购买5000个回来,用篮子装着放家里,占地方还怕吃不完坏掉了;第二种方法是去买个生蛋的母鸡,只要它活着就能一直生蛋,要几个就让它生几个。
-
程序举例:
def func(): # 这个函数就好比直接一次性生产5000个包子,生产出来的包子都在列表中, list1 = [] # 占用大量内存,如果数据量过大,则导致运行速度下降甚至撑爆内存。 for i in range(1, 5001): list1.append(f'第{i}个包子') return list1 print(func()) def gen_func(): # 使用生成器函数生产包子 for i in range(1, 5001): # 也是可以生产5000个,但并不是一次性返回 yield f'第{i}个包子' # yield配合for循环也是可以使用,next一次提取一个数据 baozi = gen_func() # 调用函数,将返回值放到baozi变量中 for i in range(1000): # 可以根据需要的数量产生,这次需要1000个包子 print(next(baozi)) # next1000次就可以获得
-
yield form
前面用到的yield都是在函数内返回一个不可变数值,但如果想next一次就返回可迭代对象中的一个元素,就需要用到yield form。举例如下:
def func(): list1 = [1, 2, 3, 4, 5, 6] # 返回的对象是个列表 yield from list1 # 如果不用yield form将返回整个列表 ret = func() for i in range(3): # 循环3次,返回列表中的前3个数据 print(next(ret))
-
-
列表推导式和生成器表达式:
-
列表推导式:用一行代码构建一个比较复杂、有规律的列表。
列表推导式分成两种模式:
-
循环模式:[变量(加工后的变量) for 变量 in iterable]
例一:一行代码实现for循环产生有规律的列表
list1=[i for i in range(1,11)] print(list1) >>>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
例二:将10以内所有整数的平方放入列表
pflist=[i**2 for i in range(1,11)] print(pflist) >>>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
例三:将50以内所有偶数放入列表
list1=[i for i in range(2,51,2)] print(list1)
例四:从Python1期到Python100期写入列表。
list2=[f'Python{i}期' for i in range(1,101)] print(list2)
-
筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
在循环模式下,满足条件的放入列表
例一:30以内能被3整除的数放入列表
list1 = [i for i in range(1, 31) if i % 3 == 0] print(list1) >>>[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
例二:过滤掉长度小于3的字符串列表,并将剩下的转换成大写
l1 = ['abc', 'a', 'h', 'inter', 'amwk', 'ce', 'sk', 'at', 'apple', 'c', 'reset'] print([i.upper() for i in l1 if len(i) >= 3]) >>>['ABC', 'INTER', 'AMWK', 'APPLE', 'RESET']
例三:含有两个'e'的所有的人名全部大写留下来
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] print([name.upper() for i in names for name in i if name.count('e')==2]) >>>['JEFFERSON', 'WESLEY', 'STEVEN', 'JENNIFER']
-
-
生成器表达式:
生成器表达式与列表推导式的写法几乎一模一样:只是将列表推导式中的方括号变成了圆括号,生成器的最大优点是节省资源。
生成器表达式也分为循环模式和筛选模式,支持多层循环构建,写法上只有一个不同:[]换成()。虽然只是写法不同,但是本质有很大区别,列表推导式非常占用内存,生成器表达式非常节省内存。
obj= (i for i in range(0,11, 2)) print(next(obj)) print(next(obj)) print(next(obj)) for i in obj: print(i,end=' ') >>>0 2 4 6 8 10
-
两种表达式的总结:
-
列表推导式:
缺点:
- 有毒,列表推导式只能构建比较复杂并且有规律的列表,不要太着迷。
- 超过三层循环才能构建成功的列表,就不建议用列表推导式。
- 排错不行,查找错误(debug)下不方便,不直观。
优点:
- 一行构建,简单
- 装逼?(太白这样觉得)
-
生成器表达式:
-
-
-
-