迭代器
可迭代的
一个含有__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会来