一、迭代器

定义:拥有 iter 和 next 方法的对象就是迭代器

迭代:是访问集合元素的一种方式,可以将集合元素中的数据’一个挨着一个取出来’叫迭代
(如for循环:–ter-- 方法返回一个迭代器,调用迭代器的–next-- 方法取值)

二、生成器

本质:就是迭代器
两种:生成器函数生成器表达式

1.生成器函数

一个包含yield关键字的函数就是一个生成器函数。并且yield不能和return公用,并且
yield只能用在函数内。
	1.生成器函数执行后得到一个生成器作为返回值,并不会执行函数体
	2.执行--next--方法后才会执行函数体,并且获得返回值
	3.--next--内置方法,内部调用生成器函数的--next--方法
	4.yield 与 return 相同的是可以返回值,但yield不会结束函数
def generator():
    print('zzz')
    yield
    
res = generator()
print(res) #<generator object generator at 0x000001D29D88D5E8>
res.__next__() #zzz

send() 方法

获取下一个值与next基本一致,只是在获取下一个值时,会给上一个yield的位置传递一个数据

注意事项:

1.第一次使用生成器时,使用next获取下一值
2.最后一个yield不能接受外部的值

2.生成器表达式
格式:与列表推导式类似,将 [ ] 改为 ()即可

egg = ('鸡蛋%s ' % i for i in range(1, 20))
print(egg)  #<generator object <genexpr> at 0x000001A742A3D5E8>

print(egg.__next__()) #鸡蛋1 
print(next(egg)) #鸡蛋2

回顾列表推导式,并与生成器比较优劣

列表推导式:
lst1 = [i**2 for i in range(1,20) if i % 2 == 0]
print(lst1) #[4, 16, 36, 64, 100, 144, 196, 256, 324]

比较 列表推导式 与 生成器
	1.返回数据类型不同
	2.生成器所占内存比列表少 (可使用sys.getsizeof()方法查看内存)

from sys import getsizeof

lst2 = [x ** 2 for x in range(2000)]
print(lst2)  
a = getsizeof(lst2)
print(a) #16560

gen = (x ** 2 for x in range(2000))
print(gen) #<generator object <genexpr> at 0x000002660A7CD5E8>
print(next(gen)) #0
print(next(gen)) #1
print(next(gen)) #4
b = getsizeof(gen)
print(b) #120

原因:
生成器一次只生成一个,所以它会比列表更有内存效率
如:当迭代列表时,python会保存完整的一个列表,而生成器不会将整个全部元素存储在内存中,并且只会在需要时’生成next/send’序列的下一个元素

三、装饰器

本质:一个闭包函数
作用:在不修改源代码的前提下,对原函数功能进行扩展
格式:@装饰器名
执行原理:先执行距离函数近的装饰器

闭包函数通常3个要求:
	1.函数嵌套定义
	2.内部函数使用外部函数的变量
	3.外部函数返回值为内部函数名
通用闭包:
def fun_out(func):
    def func_in(*args,**kwargs):
        return func(*args,*kwargs)
    return func_in