day 13 - 1 迭代器
程序员文章站
2023-11-10 21:59:34
迭代器:迭代器协议和可迭代协议,for 循环的运行逻辑,迭代器的好处 ......
迭代器
首先我们查看下列类型拥有的所有方法(会显示很多)
print(dir([])) print(dir({})) print(dir('')) print(dir(range(10)))
#求下上面列表中方法的交集 ret = set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10))) #set 转化为列表 print(ret) #可以注意到都有这两个双下方法 # __init__ __iter__ #我们来举一个双下方法的例子 #举例:列表的相加 print([1].__add__([2])) print([1]+[2]) #实际执行的是 print([1].__add__([2])) #接着我来看一下 int 类型 for i in 123: print(i) #不可被循环的会提示该报错:typeerror: 'int' object is not iterable(iterable:可迭代的) #原因是 int 类型没有该方法 __iter__
判断数据类型中是否有 __iter__ 方法
print('__iter__' in dir(int)) #false print('__iter__' in dir(bool)) #false print('__iter__' in dir(list)) print('__iter__' in dir(dict)) print('__iter__' in dir(set)) print('__iter__' in dir(tuple)) print('__iter__' in dir(enumerate([]))) print('__iter__' in dir(range(1))) #方法之间也可以相减 去重(主要是查看 并没有什么用) print(set(dir([].__iter__())) - set(dir([])))
只要是能被for循环的数据类型 就一定拥有__iter__方法,一个列表执行了__iter__()之后的返回值就是一个迭代器
print([].__iter__()) <list_iterator object at 0x0000000000a8f2e8> #iterator 迭代器,返回的是一个内存地址
#使用 __next__ 一个一个的取值 l = [1,2,3] iterator = l.__iter__() print(iterator) #列表是可以迭代的,当可迭代的后面加上 .__iter__() 方法,就可以得到一个迭代器,返回是一个内存地址 print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) #当无值可取时会抛出 stopiteration 的异常 print(iterator.__next__()) #for 循环就是一个迭代器 当无值可取时也会出现该异常,但是 for 已经帮我们处理了 #__length_hint__ 计算元素个数 print([1,'a','bbb'].__iter__().__length_hint__())
iterable 可迭代的 ——> __iter__ 只要含有 __iter__ 方法的都是可迭代的 —— 可迭代协议
[].__iter__() 迭代器 -- > __next__ 通过 next 就可以从迭代器中一个一个的取值
简单的证明一下上面的结论
from collections import iterable from collections import iterator class a: def __iter__(self):pass #在这两处添加注释 然后查看下面的输出结果 def __next__(self):pass #两次查看做下对比 a = a() print(isinstance(a,iterator)) print(isinstance(a,iterable))
迭代器的概念
迭代器协议 —— 内部含有 __next__ 和 __iter__ 方法的就是迭代器
迭代器协议和可迭代协议
要含有 __iter__ 方法的都是可迭代的 —— 可迭代协议 部含有 __next__ 和 __iter__ 方法的就是迭代器 —— 迭代器协议 可以被for循环的都是可迭代的 可迭代的内部都有 __iter__ 方法 只要是迭代器,一定是可迭代的 可迭代的 .__iter__() 方法就可以得到一个迭代器 迭代器中的 __next__() 方法可以一个一个的获取值
for 循环
for 循环其实就是在使用迭代器
只有 是可迭代对象的时候 才能用 for
当我们遇到一个新的变量,不确定能不能 for 循环的时候,就判断它是否可迭代(判断它内部有没有 __iter__ )
for 循环的运行逻辑
for i in l: pass iterator = l.__iter__() iterator.__next__()
迭代器的好处
从容器类型中一个一个的取值,会把所有的值都取到。
节省内存空间(因为它不是一下子发内容存到内存当中的,而是一条一条的读取)
迭代器并不会在内存中再占用一大块内存,
而是随着循环 每次生成一个
每次 next 每次给我一个
装饰器复习
装饰器
装饰器的作用:在不改变原来函数的调用方式的情况下,在这个函数的前后添加新的功能
完美的符合了一个开发原则:开放封闭原则
对扩展的是开放的
对修改是封闭的
基础的装饰器
from functools import wraps def wrapper(func): @wraps(func) def inner(*args,**kwargs): '''在函数被调用之前添加的代码''' ret = func(*args,**kwargs) # func是被装饰的函数 在这里被调用 '''在函数被调用之后添加的代码''' return ret return inner #使用 —— @wrapper @wrapper def func(): #inner pass
带参数的装饰器
@wrapper -- > @warapper(argument) #三层嵌套函数 def outer(形参): def wrapper(func): def inner(*args,**kwargs): '''在函数被调用之前添加的代码''' ret = func(*args,**kwargs) # func是被装饰的函数 在这里被调用 '''在函数被调用之后添加的代码''' return ret return inner return wrapper @outer(true) def func(): pass
多个装饰器装饰一个函数
def wrapper1(func): @wraps(func) def inner(*args,**kwargs): print('before 1') print('******') ret = func(*args,**kwargs) # func是被装饰的函数 在这里被调用 '''在函数被调用之后添加的代码''' return ret
def wrapper2(func): @wraps(func) def inner(*args,**kwargs): print('before 2') ret = func(*args,**kwargs) # func是被装饰的函数 在这里被调用 '''在函数被调用之后添加的代码''' return ret @wrapper1 @wrapper2 def func(): print('111')