同学,迭代器生成器了解一下
迭代器
可迭代的
一个含有__iter__方法的对象就是可迭代的,他们都可以使用for循环取值(这样说并不对,__iter__方法应返回一个迭代器).
例如:
print('__iter__' in dir([])) # True print('__iter__' in dir(())) # True print('__iter__' in dir("wfdsf")) # True print('__iter__' in dir(123)) # False
迭代器
内部含有__next__方法的可迭代对象就是迭代器 , 迭代器是可迭代的一部分.
可迭代对象调用__iter__()就形成了一个迭代器
ret = "adc".__iter__() ret2 = ret.__iter__() # 查看类型 print(type(ret)) # <class 'str_iterator'> # 查看可迭代对象与迭代器之间的差集 print(set(dir("abc".__iter__()))-set(dir("abc"))) # {'__next__', '__setstate__', '__length_hint__'} # 查看迭代器与可迭代对象的__iter__方法有什么不同 print(ret==ret2) # True # 使用__next__方法从迭代器中取值 print(ret.__next__()) # a print(ret.__next__()) # b print(ret.__next__()) # c print(ret.__next__()) # 抛出 StopIteration 异常 #利用while循环来实现for循环的功能 l = [1,2,3,4] l_iter = l.__iter__() while True: try: #处理异常 item = l_iter.__next__() print(item) except StopIteration: break
判断一个对象是不是迭代器或一个可迭代的
方式一,
判断方法__iter__是否存在于变量的方法中,存在则是可迭代的(除非你自己写了个类实现了此方法)
判断方法__next__是否存在于变量的方法中,存在则是迭代器(除非你自己写了个类实现了此方法)
print('__iter__' in dir([1,2,3,4])) # True print('__next__' in dir([1,2,3,4])) # False
方式二
判断对象是不是迭代器或可迭代对象的实例
from collections import Iterable # 可迭代对象的类 from collections import Iterator # 迭代器的类 print(isinstance([1,2,3,4],Iterable)) # True str_iter = [1,2,3,4].__iter__() print(isinstance(str_iter,Iterator)) # True print(isinstance([1,2,3,4],Iterator)) # False
迭代器的特点
- 惰性运算
- 从前到后一次取值,过程不可逆,不可重复
- 节省内存
生成器
生成器的本质就是迭代器
因此生成器具有迭代器的特点,但是生成器是我们自己写的代码
生成器函数
一个包含yield关键字的函数就是一个生成器函数。
yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
def genrator_fun1(): # 这个函数就是一个生成器函数 yield 1 yield 2 x=genrator_fun1()#x 就是一个生成器 print(type(x)) # <class 'generator'> print(x.__next__()) # 1 print(x.__next__()) # 2
监听文件末尾追加的例子:
#监听文件末尾追加的例子 def tail(): f = open('文件','r',encoding='utf-8')#打开文件 f.seek(0,2)把光标移至末尾 while True: line = f.readline()#读取内容 if line:#内容不为空 yield line#返回读取到的内容 import time time.sleep(0.1) g = tail() for i in g: print(i.strip())
send方法
send方法可以将数据传递给生成器,并返回了一个yield值
def func(): a = yield 5 # send的值会由yield前的变量接收,因为表达式会先计算右边的值,后面不会执行 # 所以说至少yield一次才能send,否则会抛出TypeError异常 yield a g = func() num = g.__next__() print(num) # 5 num2 = g.send('alex') print(num2) # alex
第一次也可以g.send(None),相当于g.__next__()
求平均数实例
def init(func): #生成器的预激装饰器 def inner(*args,**kwargs): g = func(*args,**kwargs) #func = averager g.__next__() # 先执行一次__next__() return g return inner @init def averager(): # 求平均值 total = 0.0 count = 0 average = None while True: term = yield average # 返回average, send传值给term total += term # 总和 count += 1 # 次数 average = total/count # 平均数 g_avg = averager() print(g_avg.send(10))#10 print(g_avg.send(30))#20
yield form用法
def func(): a = 'AB' b = 'CD' yield from a# 相当于for i in a:yield i yield from b# 相当于for i in b:yield i # 'A','B','C','D' # 返回了4次 g = func() for i in g: print(i)
生成器表达式
#列表推导式 y = [1,2,3,4,5,6,7,8] x = [1,4,9,16,25,36,49,64] #由列表y要得到一个列表x x = [i*i for i in y]#列表推导式 #生成器表达式 #把列表推导式的[]换成()就变成了生成器表达式。 l = ['鸡蛋%s'%i for i in range(10)] print(l)#列表表达式获得一个列表 #生成器表达式获得了一个生成器 laomuji = ('鸡蛋%s'%i for i in range(10)) for egg in laomuji: print(egg)
面向对象中使用__iter__
for循环的本质起始就是调用一个对象的__iter__()方法,获得一个迭代器,使用__next__()方法取值,并且做了异常处理,我们自己定义类时,也可以自己是实现__iter__()方法,这样我们的对象就可以被循环了.
class For: def __init__(self,list): self.list = list def __iter__(self): for i in self.list: yield i l = For([1,2,3,4,5,6]) for i in l: print(i)
在form组件中就用到了这种方法,当我们循环form实例时,他的__iter__方法循环他的字段列表,并将值yield会来