迭代器及生成器基础

迭代器

可迭代对象

list、str、tuple等,可以被遍历的(迭代)

迭代器协议

对象必须提供一个next方法,执行该方法,要么返回迭代中的下一项,要么引起StopIteration异常,以终止迭代(只能往前进行,不能回退)

现在,我们就可以说,实现了迭代器协议的对象就是可迭代对象。

实现
  • 通过在对象内部定义一个__iter__方法,有该方法的能够进行迭代
li=[1,2,3]
print(li.__iter__())
li_inter=li.__iter__()
print(li_inter.__next__()) #依次打印下一个
print(li_inter.__next__()) #依次打印下一个
print(next(li_inter))      #依次打印下一个,效果等同上面,继续上面的next
print(li_inter.__next__()) #依次打印下一个,但是超出索引引起异常
#结果#
<list_iterator object at 0x01519E50>
Traceback (most recent call last):
1
2
3
    print(li_inter.__next__())
StopIteration

不具备该方法的对象(如int),无法进行迭代

for i in 100:
    print(i)
TypeError: 'int' object is not iterable    

判断对象能否迭代

from collections.abc import  Iterable
print(isinstance([],Iterable))
print(isinstance(1,Iterable))
print(isinstance('',Iterable))
print(isinstance(False,Iterable))

True
False
True
False

测试一自定义类(可以容纳数据)是否可以进行迭代

from collections.abc import  Iterable
class DIYClass:
    def __init__(self):
        self.names=[]
    def add(self,name):
        self.names.append(name)
my_class=DIYClass()
my_class.add('Tom')
my_class.add('Bob')
print(isinstance(my_class,Iterable))	   #该类不能进行迭代	 
print(isinstance(my_class.names,Iterable)) #列表当然可以进行迭代

False
True

让自定义类可以迭代,为它补充一个__iter__方法

from collections.abc import  Iterable
class DIYClass:
    def __init__(self):
        self.names=[]
    def add(self,name):
        self.names.append(name)
    def __iter__(self):
        pass
my_class=DIYClass()
my_class.add('Tom')
my_class.add('Bob')
print(isinstance(my_class,Iterable))
for i in my_class:
    print(i)
    
True ##虽然具备—__iter__方法,返回True,但是没有具体该方法,所以遍历报错
    for i in my_class:
TypeError: iter() returned non-iterator of type 'NoneType'    

__iter__方法补充具体方式,让其可以直接进行迭代

def __iter__(self):  #具体__iter__
    return self.names.__iter__()
print(isinstance(my_class,Iterable))  #继续之前的操作
for i in my_class:
    print(i,end=' ')
    
True
Tom Bob 

在迭代一个可迭代对象的时候,实际上就是获取该对象提供的一个迭代器。然后通过该迭代器依次获取对象的每一个数据。

for . . . in . . .循环的本质

通过iter()函数,获取可迭代对象的Iterable迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值,并且将赋值item,当遇到StopIteration的异常后,退出。

应用场景

迭代器的核心就是通过next()函数调用返回下一个数据值。如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定规律计算生成。那么也就意味着可以不用依赖一个已有的数据集合Namely,无需将所有的迭代对象数据一次性缓存下来供后续使用。这样,可以节省大量的存储(内存)空间。

斐波那契数列,现在我们希望用for in 的方式来遍历数列中的前n个数

通过迭代来实现,每次迭代都可以通过数学计算生成下一个数,(2个初始值)

class FibIterator:
    """斐波那契数列迭代器"""
    def __init__(self,n):
        #记录生成的斐波那契数列的个数
        self.n=n
        #记录当前记录的索引
        self.current_index=0
        #记录2个初始值
        self.num1,self.num2=0,1
    def __next__(self):
        """调用next()函数获取下一个数"""
        if self.current_index<self.n:
            num=self.num1
            self.num1,self.num2=self.num2,self.num1+self.num2
            self.current_index+=1
            return num
        else:
            raise StopIteration
    def __iter__(self):
         return self
fib=FibIterator(10)   
for i in fib:
    print(i,end=' ')
0 1 1 2 3 5 8 13 21 34 

自定义一个迭代器

class Test:
    def __init__(self,data=1):
        self.data=data
    def __iter__(self):
        return self
    def __next__(self):
        if self.data>5:
            raise StopIteration
        else:
            self.data+=1
        return self.data
for i in Test():  ##效果类似于range
    print(i,end=' ')
2 3 4 5 6 

注:return self 返回的是类的实例

生成器

解释

生成器就是利用迭代器,在每次迭代获取数据时(通过next()方法)按照特点的规律进行生成

但是在实现一个迭代器时,关于之前迭代的状态需要我们自己记录,这样才能根据当前的状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,可以采用更简便的语法。即生成器。

生成器是一种特殊的迭代器,他比迭代器更优雅。

生成器的创建

列表 与生成器 [ ]---->( )

li=[x**2 for x in range(6)]
gen=(x**2 for x in range(6))
print(li)
print(gen)
while True:     #获取生成器元素
    try  :
        print(next(gen),end=' ')
    except StopIteration: #捕捉异常读完元素
        print('\nIt\'over.')
        break
        
[0, 1, 4, 9, 16, 25]
<generator object <genexpr> at 0x01A45EF0>  #表示这是一个生成器
0 1 4 9 16 25 
It'over.

遍历生成器

gen=(x**2 for x in range(6))
for i in gen:
    print(i,end=' ')
0 1 4 9 16 25 
生成器函数

在函数中如果出现了yield关键字,那么该函数就不再是一个普通函数而是一个生成器函数。

def foo():
    yield 1
    yield 2
f=foo()
print(next(f))  #程序会停留在对于顺序的yield后的语句
print(next(f))	#程序会停留在对于顺序的yield后的语句
print(next(f))	#超出范围的next()报错StopIteration

Traceback (most recent call last):
1
2
    print(next(f))
StopIteration    

next()与yield会进行匹配,如果next()超出范围,会抛出StopIteration异常,终止迭代,如果中途遇到return

不会继续return后面的yield,直接抛出StopIteration异常,如果return 后面有返回值,那么抛出的异常就是该返回值内容,着表示函数的返回值

def show():
    yield 1
    yield 2
    return 'over'
    yield 3
s=show()
print(next(s))
print(next(s))
print(next(s))

Traceback (most recent call last):
1
2
    print(next(s))
StopIteration: over
生成器的其他方法
close

将生成器直接到抛出异常的位置

def gen():
    yield 1
    yield 2
    yield 3
    yield 4
g = gen()
print(next(g))
print(next(g))
g.close()
print(next(g))  # 回溯
Traceback (most recent call last):
1
2
    print(next(g))  
  
StopIteration#close提前到抛出异常的位置,再调用就报错
send
  • send()的作用就是使receive赋值为其所传送的值,然后让生成器执行到下一个yield

  • 如果生成器未启动,则必须在使用send()前启动生成器,而启动的方法可以是gen.next(),也可以是gen.send(None)执行到第一个yield处。之后就可以使用send()不断的传入值了

  • 如果已启动,则send(para)的作用就是给x付志伟发送的值(send的参数),然后让生成器执行到下一个yield

  • obj.send(None)等价于next(obj)

    def gen():
        value = 0
        while True:
            receive = yield value
            if receive == "e":
                break
            value = "got:%s" % receive
    g = gen()
    print(g.send(None))
    print(g.send("aaa"))
    print(g.send(123))
    print(g.send("e"))
    
    0     #打开生成器
    Traceback (most recent call last):
    got:aaa #将字符串传给receive并以value的一定格式返回
    got:123 #将数字传给receive并以value的一定格式返回
        
        print(g.send("e")) #输入关键字,直接抛出异常终止
    StopIteration
    
总结

生成器存在边循环一边计算的机制,生成器函数为包含yield关键字的函数,它将函数又变成一个迭代器

对于生成器函数补充如下示例

def gen():
    i=0
    while i<5:
        print('in here 1.0')
        receive = yield i
        print('in here 2.0')
        print(receive,i)
        i+=1
g = gen()
print(g.send(None))      #等价于next(g)
print('~~~~~~~~~~~~~~~')
print(g.send(None))		 #等价于next(g)
print('~~~~~~~~~~~~~~~')
print(g.send(123))		 #给receive传入以123
print('~~~~~~~~~~~~~~~')
print(g.send(None))		#等价于next(g)
#结果
in here 1.0   #开启生成器
0				#在yield处停止,没有打印第二句话,也没有打印i和receive,将i=0返回
~~~~~~~~~~~~~~~
in here 2.0  #接到send指令,从上次的断点继续,打印第二句话
None 0		#没有接收参数,为None;i没有自增i=0
in here 1.0   #i完成自增i=1,在yield处停止,没有打印第二句话
1				#将i=1返回
~~~~~~~~~~~~~~~
in here 2.0  #接到send指令,从上次的断点继续,打印第二句话
123 1			#接收到send的参数123赋值给receive,i=1
in here 1.0	#i完成自增i=2,在yield处停止,没有打印第二句话
2				#将i=2返回
~~~~~~~~~~~~~~~
in here 2.0
None 2
in here 1.0
3

由上实例可以看出首先生成器函数与一般不同,但是yield与函数的return相似,每次在return(yield)终止,生成器

开启后,每次迭代停于yield处,下次迭代时,代码从yield的下一条语句开始执行。obj.send(None)等价于next(obj);开启时必须用该方法[next(obj)]开启

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值