Python生成器 百日筑基之堵漏
生成器
函数体内有yield选项的就是生成器,生成器的本质是迭代器,由于函数结构和生成器结构类似,可以通过调用判断是函数还是生成器.如下:
def fun(): yield "我是生成器" print(fun()) # 打印内容如下: <generator object fun at 0x0000000002160ed0>
生成器的优点就是节省内存.
python获取生成器的二种方式:
- 通过函数获取生成器
- 通过生成器推导式创建生成器
通过函数获取生成器
def fun(): print("fun") yield "生成器" g = fun() print(g) # 打印函数名查看是否是生成器 # 打印内容如下: <generator object fun at 0x0000000000510ed0>
从打印内容可以看出是生成器.但是发现生成器里面的内容没有被打印,那如何打印生成器内容呢?我们可以把生成器理解成迭代器的变异版,所以要打印生成器的内容,与迭代器类似,创建生成器对象后.可以使用生成器.__next__()来打印生成器内容.或者next(),send()等来打印生成器,如下:
使用.__next__()来打印生成器中的内容
def fun(): print("fun") yield "生成器" print("我在生成器的下面") g = fun() # 创建生成器对象 print(g) # 打印生成器对象 print(g.__next__()) # 打印生成器里面的内容 # 打印内容如下: <generator object fun at 0x0000000002200ed0> fun 生成器
可以发现yield下面的print语句没有被打印.到yield停止了
def fun(): print("fun") yield "生成器1" print("我在生成器1下面") yield "生成器2" print("我在生成器2的下面") g = fun() # 创建生成器对象 print(g.__next__()) print(g.__next__()) # 打印内容如下: fun 生成器1 我在生成器1下面 生成器2
由上面两个事例可以得出一个总结:就是每next一次就执行一次yield上面的代码一次,yield下面的代码不会被执行,这就是生成器的惰性机制
使用next()打印生成器内容
def fun(): print("fun") yield "生成器" print("我在生成器下面") yield "生成器2" print("我在生成器2的下面") g = fun() print(next(g)) # next(g)打印生成器内容 print(next(g)) # next(g)打印生成器内容 # 打印内容如下: fun 生成器 我在生成器下面 生成器2
与.__next__()功能类似
使用send(参数)打印生成器内容:
send方法可以给上一层的yield传递一个值,如果上一个yield没有值的话send的参数将被忽略,如果有值yield的值将被改变成当前的参数,还有需要注意的地方就是如果send(参数)做为第一次迭代,由于上一层没有yield,所以没有办法传参,会导致出现错误,错误内容如下:
typeerror: can't send non-none value to a just-started generator
我们将send(none)作为第一次调用即可.然后在第二次调用时可以传适当的参数.
如下:
def fun(): print("fun") val = yield "生成器" print("我在生成器下面") print(val) yield "生成器2" print("我在生成器2的下面") yield "生成器3" print("我在生成器3的下面") g = fun() print(g.send(none)) print(g.send("send")) print(g.send("send2")) # 打印内容如下: fun 生成器 我在生成器下面 send 生成器2 我在生成器2的下面 生成器3
生成器的基础用法:
使用for循环打印生成器对象
def fun(): print("fun") yield "生成器" print("我在生成器下面") yield "生成器2" print("我在生成器2的下面") yield "生成器3" print("我在生成器3的下面") g = fun() # 创建生成器对象 for g_buf in g: # 使用for循环打印生成器对象 print(g_buf) # 打印内容如下 fun 生成器 我在生成器下面 生成器2 我在生成器2的下面 生成器3 我在生成器3的下面
yield可以返回任何数据类型,这里以列表为事例
def fun(): list_1 = [1,2,3,4,5] yield list_1 # 将整个列表作为返回值传给生成器对象 g = fun() # 创建生成器对象 print(g.__next__()) # 打印生成器对象 # 打印内容如下: [1, 2, 3, 4, 5]
如果想要yield从列表中每次返回一个元素使用yield from 列表来实现
def fun(): list_1 = [1,2,3,4,5] yield from list_1 g = fun() # 创建生成器对象 print(g.__next__()) # 打印生成器对象内容 # 打印内容如下: 1
可以发现只打印了列表中的一个元素.可以使用for循环打印所有内容:
def fun(): list_1 = [1,2,3,4,5] yield from list_1 g = fun() for g_buf in g: print(g_buf) # 打印内容如下: 1 2 3 4 5
相当于事项了5次print(g.__next__()) # 打印生成器对象内容
推导式:
列表推导式:
如给list_1列表赋值1-20,常规做法如下:
list_1 = [] for num in range(20): list_1.append(num) print(list_1) # 打印内容如下: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
列表list_1和list_2简单的推导式如下:
list_1 = [num for num in range(20)] list_2 = ["python: %s" % num for num in range(5)] print(list_1) print(list_2) # 打印内容如下: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] ['python: 0', 'python: 1', 'python: 2', 'python: 3', 'python: 4']
列表推导式还可以进行筛选,如下:
list_1 = [num for num in range(20) if num < 5 or num == 15] print(list_1) # 打印内容如下: [0, 1, 2, 3, 4, 15]
升级一点,将一个嵌套列表中以"a"开头和以"h"开头的元素存放在一个空列表中
基础写法如下:
names = [['abc', 'abb', 'zzz'],["hello","world","xiaoming"]] list_names = [] for name_1 in names: if type(name_1) == list: for name_2 in name_1: if name_2.startswith("a") or name_2.startswith("h"): list_names.append(name_2) print(list_names) # 打印内容如下: ['abc', 'abb', 'hello']
使用列表推导法
names = [['abc', 'abb', 'zzz'],["hello","world","xiaoming"]] list_names = [name_2 for name_1 in names if type(name_1) for name_2 in name_1 if name_2.startswith("a") or\ name_2.startswith("h")] # 打印内容如下: ['abc', 'abb', 'hello']
生成器推导式:
与列表推导式类似,只不过列表是使用[],生成器推导式使用的是()
g_1 = (num for num in range(20)) print(g_1) print(g_1.__next__()) print(g_1.__next__()) # 打印内容如下: <generator object <genexpr> at 0x00000000026a0ed0> 0 1
从打印内容和使用__next__()方法可以看出g_1是列表表达式.
可以使用for循环打印生成器对象
g_1 = (num for num in range(20)) for num in g_1: print(num)
生成器的筛选与列表推导式用法一样,只不过是()
如下:过滤1-20内的所有偶数
g_1 = (num for num in range(20) if num % 2 == 0)
升级:与上面列表推导式升级练法类似.
names = [['abc', 'abb', 'zzz'],["hello","world","xiaoming"]] list_names = (name_2 for name_1 in names if type(name_1) for name_2 in name_1 if name_2.startswith("a") or\ name_2.startswith("h")) # 创建生成器对象 print(list_names) for buf in list_names: print(buf) # 打印内容下: <generator object <genexpr> at 0x0000000002150ed0> abc abb hello
生成器表达式和列表推导式的区别:
- 列表推导式比较耗内存,一次性加载.生成器表达式几乎不占用内存.使用的时候才分配和使用内存
- 得到的值不一样,列表推导式得到的是一个列表.生成器表达式获取的是一个生成器
字典推导式:
list_1 = ["电视剧","电影"] list_2 = ["上海滩","黄飞鸿"] dict_1 = {list_1[i]:list_2[i] for i in range(len(list_1))} print(dict_1) # 打印内容如下: {'电视剧': '上海滩', '电影': '黄飞鸿'}
集合推导式:
集合的特点;无序,不重复 所以集合推导式自带去重功能
list_1 = [1,2,3,4,2,3,5] set_1 = {i for i in list_1} # 集合推导式 print(set_1) # 打印内容如下: {1, 2, 3, 4, 5}
总结:
- 推导式有列表推导式,生成器推导式,字典推导式,集合推导式
- 生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)
- 生成器表达式可以直接获取到生成器对象,生成器对象具有惰性,每次只能打印一个生成器内容,可以使用for循环打印生成器所有的内容.
下一篇: Redis基本数据结构及应用案例