'''
生成器

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。
但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。

为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)
生成器是一类特殊的迭代器。
'''

import sys
import time


l = [x**2 for x in range(10)]
print(l)

g = (x**2 for x in range(10))
print(g)

'''
>>> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> <generator object <genexpr> at 0x0000029D827E6A98>

创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。
我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。
'''

print(next(g))
print(next(g))
'''
>>> 0
>>> 1
'''

for n in g:
    print(n)


'''
yield

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。
简单来说:只要在def中有yield关键字的 就称为 生成器
'''

def fib(n):
    current = 0
    num1, num2 = 0, 1
    while current < n:
        num = num1
        num1, num2 = num2, num1 + num2
        current += 1
        yield num
    
    return 'success...'

print('fib:')
f = fib(6)
for n in f:
    print(n)

'''
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。
如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

while True:
  try:
    x = next(f)
    print(f"value:{x}")
  except StopIteration as e:
    print(f"生成器返回值:{e.value}")
    break

'''

# 生成器和列表对比
start_time = time.perf_counter()
l = [i for i in range(2, 10001, 2)]
cost_time = time.perf_counter() - start_time
print(f"list创建时间:{cost_time}")
print(f"list内存开销:{sys.getsizeof(l)}字节")

start_time1 = time.perf_counter()
g = (i for i in range(2, 10001, 2))
cost_time1 = time.perf_counter() - start_time1
print(f"生成器创建时间:{cost_time1}")
print(f"生成器内存开销:{sys.getsizeof(g)}字节")

'''
>>> list创建时间:0.0001462
>>> list内存开销:43040字节
>>> 生成器创建时间:3.6000000000000333e-06
>>> 生成器内存开销:88字节
'''

'''
send唤醒
'''
def gen():
    i = 0
    while i < 5:
        temp = yield i
        print(temp)
        i += 1
print('send:')
A = gen()
print(next(A))
print(A.send('haha'))
print(next(A))
print(A.send('heihei'))

'''
>>> 0
>>> haha
>>> 1
>>> None
>>> 2
>>> heihei
>>> 3
'''