# <======== 生成器概念 ========>
详细介绍:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000
# 这种一边循环一边计算的机制,称为生成器: generator
# 可以被next()函数调用并不断返回下一个值的对象称为 迭代器: Iterator
# <======== 为何使用生成器? ========>
"""
(1) 节省内存空间 : 只为了要前几个数据 没必要把所有的数据都遍历出来,一一放在像列表这样的集合类型数据中 它还得一个一个往内存里面装 如果有宇宙级别的数据量,还能都放下么?
(2) 生成器可以算出下一个数据:按照某种算法推算出来,可以在循环的过程中不断推算出后续无限的数据元素,
而你只需要往内存里存一个生成器 666
"""
# <======== 一.生成器定义 ========>
L = [x * x for x in range(10)] #相当于 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# print(L) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 这个是列表推导式
# 第一种
g = (x for x in range(10)) #generator
# print(g) #<generator object <genexpr> at 0x7f4b6e9fbf10> generator为生成器
"""
# 总结1:定义一个生成器的基本形式是 ( 推导式 )
外边一个圆括号 里面是推导式 区别于 (1,2,3,4) 后者它是元祖~
g = ( 1,2,3,4 ) # 不是生成器
g = [x * x for x in range(10)] # 不是生成器
g = ( x for x in range(10) ) # 是生成器
"""
# 第二种 只要一个函数当中含有 yield 这样的关键字,此刻的函数odd不在是函数了 而叫做生成器 (先记住如下格式写法)
def odd():
print('step 1')
yield 1
print('step 2')
yield 3
print('step 3')
yield(5)
"""
# 总结2: yield 有点像 return 意思 都是到这句话就停了 就把值返回了,
# 但是yield不同在于 再次执行这个生成器时候会从上一次执行的位置继续向下走,它会记住上一次离开时的状态.
# 每次返回退出 都会记住当前代码执行的位置,在继续执行的时候会从上一次的位置继续向下走
yield 5 和 yield(5) 2种写法都可以 yield 5 更像 return 5 的写法
"""
# <======== 二.获取生成器中的元素 ========>
# 方法1 可以使用next(生成器) 可以得到生成器里面的下一个元素
print( next(g) ) #0
print( next(g) ) #1
print( next(g) ) #2
print( next(g) ) #3
print( next(g) ) #4
"""
.
.
.
print( next(g) )
如果超出了生成器的边界 如同超过了一个元祖的边界 会报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
"""
# 方法2 可以使用for 循环 因为这个生成器本身 也是可迭代对象 (即可循环对象) 不必担心越界问题
print("<==fengexian1==>")
g = (x * x for x in range(10)) # 从range(10) 取出一个作为x 然后x*x作为最后值拿出来
for n in g:
print(n)
# 方法3 如果一个函数当中有yield这样的关键字,此刻的函数odd不在是函数了 而是生成器
print("<==fengexian2==>")
def odd():
print('step 1')
yield 1
print('step 2')
yield 3
print('step 3')
yield 5
# 想要获取元素:先实例化一个生成器对象 o是生成器对象 <generator object odd at 0x1022ef948>
o = odd()
# (1)可以使用next来获取 例如
print(next(o)) #step 1 1
print(next(o)) #step 2 3
print(next(o)) #step 3 5
# print(next(o)) #越界 报错了...
# (2)避免越界尴尬 使用for循环
print("<==fengexian3==>")
p = odd()
for n in p:
print(n)
# <======== 三.生成器基础案例 ========>
# 他来表示一个无量无边的数据世界 应用在宇宙级别的数据量上 (如:恒河沙,阿僧祗,那由他)
print("<==fengexian4==>")
def shengchengqi(max):
n=0
while n < max:
yield n
n+=1
obj = shengchengqi(10) #如果你写999999**999999 内存因太小会炸
for i in obj:
print(i)
# 如果你想为这个生成器 在执行结束的时候 添加一个最终的返回值
# 即return的效果 正常生成器是无法返回的 要依靠越界捕获异常 来实现
"""
g = shengchengqi(10)
while True:
x = next(g)
print('g:', x)
Traceback (most recent call last):
File "yield.py", line 110, in <module>
x = next(g)
StopIteration
我们要的就是这个 StopIteration 异常 所以改写如下
"""
print("<==fengexian5==>")
def shengchengqi(max):
n=0
while n < max:
yield n
n+=1
return "我出来了"
s = shengchengqi(10)
while True:
try:
x = next(s)
print('g:', x)
except StopIteration as e:
# 这个 e.value 是来获取return 返回值的
print('用e.value来获取return的返回值:', e.value)
break
# ###########################################~经典算法~#############################################
# <======== 斐波那契数列 ========>
"""
概念:
除第一个和第二个数外,任意一个数都可由前两个数相加得到:
如:1, 1, 2, 3, 5, 8, 13, 21, 34 ... ...
"""
def fib(max):
n, a, b = 0, 0, 1 # 即n=0 用于while计数 a记录上一次的变量值 b作为下一次要打印的数字 开始分别初始化为0和1
t = 0
while n < max:
yield b
a , b = b , a+b #先把b赋值给a 再把a+b赋值给b 好处就是相加时候用的是a与b的初始化值 而不是经过交换后的ab值 即展开代码如下
"""
t = a
a = b
b = b+t
"""
n = n + 1
obj = fib(6)
# print(next(obj))
for i in obj:
print(i)
# <======== 杨辉三角 ========>
"""
概念:
1
/ \
1 1
/ \ / \
1 2 1
/ \ / \ / \
1 3 3 1
/ \ / \ / \ / \
1 4 6 4 1
/ \ / \ / \ / \ / \
1 5 10 10 5 1
把每一行看做一个list,试写一个 generator ,不断输出下一行的list:
"""
print("<==fengexian1==>")
def yanghuisanjiao(max):
L = [1]
i=0
while i<max:
yield L
L = [1] +[ L[i]+L[i+1] for i in range( len(L)-1 ) ] + [1]
i+=1
obj = yanghuisanjiao(5)
for j in obj:
print(j)
"""
L = [1]+[]+[2]
print(L) #[1,2]
解析:
[1,1]
L[0] + L[1] = 1+1 = 2 #0
[1]+[2]+[1] = [1,2,1]
.
[1,2,1]
L[0] + L[1] = 1+2 = 3 #0
L[1] + L[2] = 2+1 = 3 #1
[1]+[3]+[3]+[1] = [1,3,3,1]
.
[1,3,3,1]
L[0] + L[1] = 1+3 = 4 #0
L[1] + L[2] = 3+3 = 6 #1
L[2] + L[3] = 3+1 = 4 #2
[1] + [4] + [6] + [4] + [1] = [1,4,6,4,1]
.
.
.
"""
# <======== 背下来 大概率面试题 ========>
# 协程
"""
Python对协程的支持是通过generator实现的。
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:
"""