迭代器(iterator)
迭代器协议:必须拥有__iter__方法和__next__方法。
迭代的概念
#迭代器即迭代的工具,那什么是迭代呢?#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值while True: #只是单纯地重复,因而不是迭代 print('===>') L=[1,2,3]count=0while count < len(L): #迭代 print(l[count]) count+=1
#dir() ----(展示所拥有的所有方法) print(dir([1,2])) print(dir((2,3)))print(dir({ 'a':1,'b':2}))print(dir({1,2,3,4}))##以下所展示的有双下方法:如'__add__', '__class__'它们是Python解释器的内部方法,也称为双下方法。是由C语言写的,可能不止一种方式调用它。
# ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] # ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] # ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']
##双下方法:
print([1].__add__([2]))print([1]+[2])###[1, 2][1, 2]
由上可以看出,可以被for循环的都是可迭代的,要想可迭代,内部必须要有一个__iter__方法。
首先,判断 int 类型有没有__iter__方法:
print("__iter__" in dir(int))# False
##执行以下__iter__方法:
print([1,2].__iter__())##生产一个迭代器
得到了一个list_iterator,此时我们又得到了一个新的名词——iterator。(迭代器)
同样的,可以对字典、集合、元祖进行__iter__操作
print((1,2,3).__iter__())print({ 'a':1,'b':2}.__iter__())print({1,2,3}.__iter__())## #
同样的得到了tuple_iterator、dict_keyiterator、set_iterator,这就说明了它们都是可迭代的。
可以通过集合求差集来看迭代器与可迭代对象质检的差别。
print(set(dir([1,2].__iter__()))-set(dir([1,2])))# {'__next__', '__length_hint__', '__setstate__'}
迭代器:必须拥有__iter__方法和__next__方法
print("__iter__" in dir([].__iter__()))print("__next__" in dir([].__iter__()))###TrueTrue
############ from collections import Iterablefrom collections import Iteratorprint(isinstance([],Iterator))print(isinstance([],Iterable ))############FalseTrue列表是可迭代的,但不是一个迭代器
#####或者创造一个数据类型:
from collections import Iterablefrom collections import Iteratorclass A: def __iter__(self):pass def __next__(self):passa = A()print(isinstance(a,Iterator))print(isinstance(a,Iterable ))###TrueTrue 再次说明,迭代器:必须拥有__iter__方法和__next__方法 PS:可迭代的.__iter__()方法就可以得到一个迭代器 迭代器中的__next__()方法可以一个一个获取值
######################################补充说明###############################################
#1、为何要有迭代器?对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器#2、什么是可迭代对象?可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下'hello'.__iter__(1,2,3).__iter__[1,2,3].__iter__{ 'a':1}.__iter__{ 'a','b'}.__iter__open('a.txt').__iter__#3、什么是迭代器对象?可迭代对象执行obj.__iter__()得到的结果就是迭代器对象而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象文件类型是迭代器对象open('a.txt').__iter__()open('a.txt').__next__()#4、注意:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
迭代器的作用:
dic={ 'a':1,'b':2,'c':3}iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身iter_dic.__iter__() is iter_dic #Trueprint(iter_dic.__next__()) #等同于next(iter_dic)print(iter_dic.__next__()) #等同于next(iter_dic)print(iter_dic.__next__()) #等同于next(iter_dic)# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志#有了迭代器,我们就可以不依赖索引迭代取值了iter_dic=dic.__iter__()while 1: try: k=next(iter_dic) print(dic[k]) except StopIteration: break #这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环
for循环:(for循环其实就是在使用迭代器)
#基于for循环,我们可以完全不再依赖索引去取值了dic={ 'a':1,'b':2,'c':3}for k in dic: print(dic[k])#for循环的工作原理#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码#3: 重复过程2,直到捕捉到异常StopIteration,结束循环
迭代器的优缺点:
#优点: - 提供一种统一的、不依赖于索引的迭代方式 (方便使用,从容器数据类型中一个一个取值,并把所有的值都取到) - 惰性计算,节省内存(迭代器并不会在内存中占用一大块内存来保存数据,而是随着循环,每次只生成一个数据)#缺点: - 无法获取长度(只有在next完毕才知道到底有几个值) - 一次性的,只能往后走,不能往前退
#只有可迭代对象的时候才能用for循环
#当遇到一个变量,不确定是否能用for循环是,先判断它是否可迭代
#############################################################################################
生成器
什么是生成器(生成器的本质是迭代器)
#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码 #含有yield关键字的函数就是 生成器函数 #调用生成器函数的时候,函数不执行,而是返回一个生成器 #每次调研阶段next方法,就会取到一个值得 #直达取到最后一个,再采用next方法就会报错 def generator():
print('====>first') yield 1 print('====>second') yield 2 print('====>third') yield 3 print('====>end')g = generator()print(g) #print(g.__next__())print(g.__next__())print(g.__next__()) ################ 或者: for i in g: print(i)##### ====>first1====>second2====>third3
##wahaha:
# 哇哈哈%idef wahaha(): for i in range(100): yield "哇哈哈%d"%ig = wahaha()count = 0for i in g: count += 1 print(i) if count > 10: breakprint("****",g.__next__()) #生成器记录当前的位置以及下一步要走的位置###哇哈哈0哇哈哈1哇哈哈2哇哈哈3哇哈哈4哇哈哈5哇哈哈6哇哈哈7哇哈哈8哇哈哈9哇哈哈10**** 哇哈哈11
#监听过滤文件,文件有“Python”就打印def tail(filename): f = open(filename,encoding="utf-8") while 1: line = f.readline() if line.strip(): yield line.strip()g = tail("file")for i in g: if "python" in i: print("$$$$$",i)
生成器表现形式:
1.生成器函数
#含有yield关键字的函数就是生成器函数
#特点:调用函数之后,函数不执行,而是返回一个生成器;每次调用next方法的时候会取到一个值;直到取到最后一个,再执行next会报错
2.生成器表达式
#从生成器中取值的方法:next;for;数据类型的强制转换,占内存。
生成器进阶:
send获取下一个值的效果和next基本一样,只是在获取下一个值的时候,给上一个yield的位置传递一个数据
使用send的注意事项
1)、send作用范围和next一样
2)、第一次使用生成器的时候,是用next方法获取下一个值
3)、最后一个yield不能接受外部的值
def generator(): print(123) content=yield 1 print("========",content) arg = yield 2 '''''' yield g = generator()ret = g.__next__()print("$$$$$$$$",ret)ret = g.send("Hello!") # send 的效果和next一样print("########",ret)###123$$$$$$$$ 1======== Hello!######## 2
##用生成器,获取移动平均值#移动平均值
def average(): sum = 0 count = 0 avg = 0 while 1: num = yield avg sum += num count += 1 avg = sum/countavg_g = average()avg_g.__next__()print(avg_g.send(10))print(avg_g.send(20) print(avg_g.send(30))
### 10.0 15.0 20.0
#带装饰器的移动平均值;#在调用被装饰生成器函数的时候首先用next激活生成器 #预激生成器:def init(func): def inner(*args,**kwargs): g = func(*args,**kwargs) next(g) #g.__next__() return g return inner@initdef average(): sum = 0 count = 0 avg = 0 while 1: num = yield avg sum += num count += 1 avg = sum/countavg_g = average()print(avg_g.send(10))print(avg_g.send(20))print(avg_g.send(30))
### 10.0 15.0 20.0
#在调用被装饰生成器函数的时候首先用next激活生成器
###yield from
##
def gen1(): for c in 'AB': yield c for i in range(3): yield i print(list(gen1())) #数据类型的强制转换 #['A', 'B', 0, 1, 2] ## def gen2(): yield from 'AB' yield from range(3) print(list(gen2())) ##
['A', 'B', 0, 1, 2]
#python3的新功能(yield from)def generator(): a = "AB" b = "22" yield from a yield from bg = generator()for i in g: print(i)##AB22
列表推导式和生成器表达式
#列表推导式egg_list = ["egg %d" %i for i in range(10)]print(egg_list)#或者用for循环egg_list = []for i in range(10): egg_list.append("egg %d" %i)print(egg_list)print([i*i for i in range(10)])###['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9']['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9'][0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
##生成器表达式
#生成器表达式g = (i for i in range(10))print(g) #返回一个生成器,几乎不占用内存for i in g: print(i)##at 0x00000262E6CE6308>0123456789
###各种推导式:
[满足条件的元素 for 元素 in 可迭代的数据类型 if 元素满足的相关条件 ] #筛选功能
例一:30以内所有能被3整除的数
multiples = [i for i in range(30) if i % 3 is 0]print(multiples)# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
例二:30以内所有能被3整除的数的平方
def squared(x): return x*xmultiples = [squared(i) for i in range(30) if i % 3 is 0]print(multiples) 或者:
lst = [i*i for i in range(30) if i % 3 ==0]print(lst)##[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]
例三:找到嵌套列表中名字含有两个‘e’的所有名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]print([name for lst in names for name in lst if name.count('e') >= 2]) # 注意遍历顺序,这是实现的关键 #['Jefferson', 'Wesley', 'Steven', 'Jennifer']