生成器
1、Python 中提供的 生成器:
本质:我们自己写的能实现迭代器功能的东西,就叫生成器。
1.1生成器函数:
常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果。在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
1.2.生成器表达式:
类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
2、生成器Generator:
本质:迭代器 ( 所以自带了 __iter__方法和 __next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
3、生成器函数:
划重点:
① 必须含有yield关键字
② 执行之后会得到一个生成器对象,作为返回值
def generator(): print('123') yield 'ab' ret = generator() print(ret) # <generator object generator at 0x000001DA911EC3B8> print(ret.__next__()) # 第一次执行函数体内的代码,如上①输出123 ②return ab
def wahaha(): for i in range(2000): yield '娃哈哈%s'%i g = wahaha() count = 0 for i in g: # 循环迭代输出 count += 1 print(i) if count > 100: break print('*********',g.__next__()) #********* 娃哈哈101
4、field关键字
4.1 yield和return的区别
# 在一个函数里return只能执行一次,return之后函数就彻底结束了: def test_return(): return 1 return 2 # 永不执行 return 3 # 永不执行 def test_return2(): for i in range(10): return i # 只能返回0,函数就结束了 —————————————————————————————————————— # yield之后可以保存函数的运行状态,下次继续执行:
【在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行】
def test_yield(): yield 1 yield 2 # 下次next()后执行 yield 3 # 下次next()后执行 def test_yield2(): for i in range(10): yield i # 每调用一次next()就会依次弹出0~9
4.2 yield的作用
# ① yield把函数变成了生成器(生成器就是迭代器)。 # ② 为函数封装好了__iter__和__next__方法,把函数的执行结果做成了迭代器。 # ③ 遵循迭代器的取值方式 — obj.__next__(),触发的是函数的执行。函数暂停与继续执行的状态都是由yield保存的。
# 倒计时的例子 def countdown(n): print("倒计时开始") while n > 0: yield n n -= 1 print("发射") g = countdown(5) print(g.__next__()) # 打印"倒计时开始" 返回5 (此时n=5) print(g.__next__()) # 返回4 (此时n=4) print(g.__next__()) # 返回3 (此时n=3) print(g.__next__()) # 返回2 (此时n=2) print(g.__next__()) # 返回1 (此时n=1) print(g.__next__()) # 打印"发射" 抛出StopIteration异常(此时n=0)
4.3 yield from:在一个生成器中引用另外一个生成器
yield from后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。
详细查看 https://www.cnblogs.com/wongbingming/p/9085268.html
def generator(): a = '12312312' b = 'asdhughiwu' yield from a yield from b g = generator() for i in g: print(i) # 运行结果: 1 2 3 1 2 3 1 2 a s d h u g h i w u
astr='ABC' # 字符串 alist=[1,2,3] # 列表 adict={"name":"wangbm","age":18} # 字典 agen=(i for i in range(4,8)) # 生成器 def gen(*args, **kwargs): for item in args: yield item # 此处返回的是'ABC',[1,2,3],{"name":"wangbm","age":18},<generator object <genexpr> at 0x000001D0A010C3B8> yield from item # 相当于 for i in item:yield i new_list=gen(astr, alist, adict, agen) print(list(new_list)) # 运行结果: # ['ABC', 'A', 'B', 'C', [1, 2, 3], 1, 2, 3, {'name': 'wangbm', 'age': 18}, 'name', 'age', <generator object <genexpr> at 0x00000256B8D6C3B8>, 4, 5, 6, 7]
5、send关键字
yield可以返回值,也可以接收值。
通过生成器的send方法可以给yield传值。
# 必须先执行__next__,才能执行send() # send两个作用: # 1.给yield传值 # 2.继续执行函数 def eat(name): print('%s要开始吃了!' % name) while 1: food = yield print('{0}在吃{1}'.format(name, food)) a = eat('alex') a.__next__() # 初始化,让函数暂停在yield处 a.send('包子') a.send('饺子')
6、生成器表达式
详见:https://www.cnblogs.com/timetellu/p/10677743.html
把列表解析的[ ]换成( )得到的就是生成器表达式:
sum(x ** 2 for x in range(4)) # 不用多此一举构造一个列表 # sum([x ** 2 for x in range(4)])
7、生成器相关面试题
# 只能取一次值 def demo(): for i in range(10): yield i g = demo() g1 = (i for i in g) g2 = (i for i in g1) print(list(g1)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(list(g2)) # [ ]
def add(n,i): return n+i def test(): for i in range(4): # [0,1,2,3] yield i g = test() for n in [1,10]: g = (add(n,i) for i in g) print(list(g)) #到list(g)才执行,向上寻找g,取得是for循环最后一步的执行结果: # 执行g = (add(n,i) for i in (add(n,i) for i in test())) (此时n=10) 运行结果: [20, 21, 22, 23] ### for循环套生成器表达式的,执行过程拆开 # n = 1时, # g0 = (add(n,i) for i in g) # n = 10时, # g1 = (add(n,i) for i in g0) # g = (add(n,i) for i in (add(n,i) for i in test())) 此时n = 10 # g = (add(10,i) for i in (10,11,12,13))