迭代器(iterator)
迭代器协议:必须拥有__iter__方法和__next__方法。
迭代的概念
#迭代器即迭代的工具,那什么是迭代呢? #迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 while True: #只是单纯地重复,因而不是迭代 print('===>') L=[1,2,3] count=0 while 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 object at 0x000001A68B0210B8> #生产一个迭代器
得到了一个list_iterator,此时我们又得到了一个新的名词——iterator。(迭代器)
同样的,可以对字典、集合、元祖进行__iter__操作
print((1,2,3).__iter__()) print({'a':1,'b':2}.__iter__()) print({1,2,3}.__iter__()) # <tuple_iterator object at 0x0000021688F61080> # <dict_keyiterator object at 0x0000021688E8A4A8> # <set_iterator object at 0x0000021688F60630>
同样的得到了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__())) ### True True
############
from collections import Iterable from collections import Iterator print(isinstance([],Iterator)) print(isinstance([],Iterable )) ############ False True 列表是可迭代的,但不是一个迭代器
#####或者创造一个数据类型:
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 )) ### True True
再次说明,迭代器:必须拥有__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 #True print(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) #<generator object generator at 0x00000299A9D16AF0> print(g.__next__()) print(g.__next__()) print(g.__next__())
################
或者:
for i in g:
print(i) ##### <generator object generator at 0x00000299A9D16AF0> ====>first 1 ====>second 2 ====>third 3
##wahaha:
# 哇哈哈%i def wahaha(): for i in range(100): yield "哇哈哈%d"%i g = wahaha() count = 0 for i in g: count += 1 print(i) if count > 10: break print("****",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/count avg_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 @init def average(): sum = 0 count = 0 avg = 0 while 1: num = yield avg sum += num count += 1 avg = sum/count avg_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 b g = generator() for i in g: print(i) ## A B 2 2
列表推导式和生成器表达式
#列表推导式 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) ## <generator object <genexpr> at 0x00000262E6CE6308> 0 1 2 3 4 5 6 7 8 9
###各种推导式:
[满足条件的元素 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*x multiples = [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']