Python迭代器和生成器

 for...in...循环执行过程

for...in...本质:调用可迭代对象的__iter__方法,获取这个方法的返回值(返回值是一个带有__next__方法的对象),然后不断调用这个对象的__next__方法。因此__next__方法需要想办法中止循环

from collections.abc import Iterable,Iterator
class A:
    def __next__(self):
        return 'hello'

class B:
    def __init__(self,x):
        self.x = x
    def __iter__(self):
        return A()
a= A()
b=B(1)
print(isinstance(a,Iterable))#False
print(isinstance(b,Iterable))#True
print(isinstance(b,Iterator))#False
"""
hello
hello
hello
........
"""
for i in b:
    print(i)

__iter__方法也可以返回自身self,此时该对象必须实现__next__方法:

from collections.abc import Iterable,Iterator
class B:
    def __init__(self,x):
        self.x = x
    def __iter__(self):
        return self
    def __next__(self):
        return 'hello'
b=B(1)
print(isinstance(b,Iterable))#True
print(isinstance(b,Iterator))#True
"""
hello
hello
hello
........
"""
for i in b:
    print(i)

此时对象b既是可迭代的对象又是迭代器对象

from collections.abc import Iterable,Iterator
class B:
    def __init__(self,x):
        self.num =0
        self.x = x
    def __iter__(self):
        return self
    def __next__(self):
        if self.num < len(self.x):
            nt = self.x[self.num]
        else:
            raise StopIteration
        self.num += 1
        return nt
b=B([1,23,'zs','sd'])
print(isinstance(b,Iterable))#True
print(isinstance(b,Iterator))#True
"""
hello
hello
hello
........
"""
for i in b:
    print(i)

在__next__方法中返回可以迭代的元素,也是for...in..遍历的元素。需要中止无限循环,抛出raise StopIteration异常

自己写range函数:

class My_range:
    def __init__(self,x):
        self.x = x
        self.count = 0

    def __iter__(self):
        return self
    def __next__(self):
        self.count += 1
        if self.count <= self.x:
            return self.count
        else:
            raise StopIteration
for i in My_range(10):
    print(i)
”“”
1
2
3
4
5
6
7
8
9
10
“”“

__iter__方法,获取这个方法的返回值(返回值是一个带有__next__方法的对象),然后不断调用这个对象的__next__方法,获得返回值

  •  __iter__ : 返回迭代器本身,实际上相当于 return self,以便在应该使用可迭代对象的地方使用迭代器。例如在for循环中。
  •         __next__ : 返回下一个可用元素,如果没有元素了,则抛出StopIteration异常,决定了迭代器的迭代规则。

        __iter__() 只会被调用一次,而 _next_() 会被调用 n 次,直到出现StopIteration异常。
 

一、迭代器

python中两个迭代的概念,一个叫做迭代器(Iterator),一个叫做可迭代对象(Iterable)

当我们实现了迭代器之后,就可以使用for循环进行遍历了。我们平常使用的字符串,列表,元组和字典等,底层都实现了迭代器。我们可以通过instance来判断

from collections.abc import Iterable,Iterator


s = "abcdefgh"
print(isinstance(s,Iterable)) # True
print(isinstance(s,Iterator)) # False

l = [1,2,3,4,5,6,7,8]
print(isinstance(s,Iterable)) # True
print(isinstance(s,Iterator)) # False

t = (1,2,3,4,5,6,7,8)
print(isinstance(s,Iterable)) # True
print(isinstance(s,Iterator)) # False

发现字符串,列表和元组并不是迭代器,而是迭代对象。

1.1、迭代器介绍

迭代器的实现非常简单,只需要实现__iter__和__next__这两个魔法函数即可,

  • 调用迭代器对象的 __iter__方法得到还是迭代器对象本身,就跟没调用一样
  • 调用迭代器对象的__next__方法返回下一个值,不依赖索引
  • 可以一直调用__next__直到取干净,最后抛出异常StopIteration(停止迭代)
     

迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完。

迭代器有两个方法:iter()和next()方法。

iter(可迭代对象):可以获得一个可迭代对象的迭代器

next(迭代器):可以获得迭代器对象的元素

class My_range:
    def __init__(self,x):
        self.x = x
        self.count = 0

    def __iter__(self):
        return self
    def __next__(self):
        self.count += 1
        if self.count <= self.x:
            return self.count
        else:
            raise StopIteration
m = My_range(5)  #m实现了__iter__方法,可迭代对象
i = iter(m) #获得可迭代对象m的迭代器
print(next(i))#1
print(next(i))#2
print(next(i))#3
print(next(i))#4
print(next(i))#5

其中:

#获得可迭代对象m的迭代器

i = iter(m)   相当于m.__iter__()----->返回的是self本身

next(i)        相当于m.__next__() ----->返回的是__next__()返回值。每次循环都会访问调用

1.2、可迭代的对象 

类似于list、tuple、str 等类型的数据可以使用for …… in…… 的循环遍历语法从其中依次拿到数据并进行使用,我们把这个过程称为遍历,也称迭代。python中可迭代的对象有list(列表)、tuple(元组)、dirt(字典)、str(字符串)set等。

mylist = [1,2,3,4,5]
mydirt = {
    "name":"张三",
    "sex":"男",
    "age":18
    }
    
mytuple = (1,2,3,4,5)
myset = {1,2,3,3,4}
 
for i in mylist:
    print(i)
 
for i in mytuple:
    print(i)
 
for i in myset:
    print(i)
 
for i,j in mydirt.items():
    print("%s:%s" % (i,j))
 
 

除此之外,也可以创建一个可迭代的对象只要此对象含有__iter__方法,那么它就是一个可迭代的对象,如下面的例子:

其中定义了一个__iter__方法,我们通过isinstance()函数以及Iterable来判断由Classmate类创建的class1对象是否是可迭代的对象:若class1是一个Iterable(可迭代对象)则结果返回为True;否则,结果为False。

from collections import Iterable
 
 
class Classmate(object):
    """定义一个同学类"""
 
    def __init__(self):
        self.name = list()
        self.name_num = 0
 
    def add(self,name):
        self.name.append(name)
    
    def __iter__(self):
        pass
 
    
class1 =  Classmate()
class1.add("张三")
class1.add("李四")
class1.add("王五")
 
print("判断是否是可迭代的对象:", isinstance(class1,Iterable))


 

1.3、创建迭代器对象 

一个类(对象)只要含有“__iter__”、"__next__"两个方法,就将其称为迭代器。__iter__方法返回一个特殊的迭代器对象,而这个迭代器对象自动实现了_next__方法,并返回一个值,最后通过抛出异常StopIteration来结束迭代。我们来给上一个例子增加__next__方法:
 

from collections import Iterable
from collections import Iterator
 
 
class Classmate(object):
    """定义一个同学类"""
 
    def __init__(self):
        self.name = list()
        self.name_num = 0
 
    def add(self,name):
        self.name.append(name)
    
    def __iter__(self):
        pass
 
    def __next__(self):
       pass
 
 
class1 =  Classmate()
class1.add("张三")
class1.add("李四")
class1.add("王五")
 
print("判断是否是可迭代的对象:", isinstance(class1,Iterable))
 
print("判断是否是迭代器:", isinstance(class1,Iterator))
 
 

只是名义上的 可迭代对象/迭代器 还不够,具有相应的功能才算是完整。首先,对于__iter__方法,它需要具有一个可以返回一个迭代器对象的功能(这个对象可以是自己(前提是本身就是一个迭代器),也可以是其它迭代器);

对于__next__方法,它需要标记并返回下一个迭代器对象。代码如下(为防止迭代速度过快,我们添加sleep来控制速度):
 

from collections import Iterable
from collections import Iterator
import time
 
 
class Classmate(object):
    """定义一个同学类"""
 
    def __init__(self):
        self.name = list()
        self.name_num = 0
 
    def add(self,name):
        self.name.append(name)
    
    def __iter__(self):
        return self   # 返回本身
 
    def __next__(self):
       
       # 记忆性返回数据
       if self.name_num < len(self.name):
           ret = self.name[self.name_num]
           self.name_num += 1
           return ret
 
 
 
class1 =  Classmate()
class1.add("张三")
class1.add("李四")
class1.add("王五")
 
print("判断是否是可迭代的对象:", isinstance(class1,Iterable))
 
print("判断是否是迭代器:", isinstance(class1,Iterator))
 
for name in class1:
    print(name)
    time.sleep(1)

iter(iterable),iterable为可迭代的对象。如iter([1, 2, 3])--->获得迭代器对象
next(iterator) :iterator为迭代器对象。如next(iter([1, 2, 3]))

即:

  • iter_ obj = iter([1,2,3])
  • next(iter_obj)

1.4、使用迭代器实现斐波那契数列

class Fab:
    def __init__(self,n):
        self.n = n
        self.forward = 0
        self.cur = 1
        self.count = 0
    def __iter__(self):
        return self
    def __next__(self):
        self.count += 1
        if self.count <= self.n:
            self.forward,self.cur = self.cur,self.forward+self.cur
        # time.sleep(1)
        else:
            raise StopIteration
        return  self.cur
for fab in Fab(8):
    print(fab)

二、生成器

迭代器和生成器以及上下文管理器都是python的高级特性,它们的区别在于实例化出来的对象中是否含有一些函数。在for循环中不管是传入的可迭代对象还是迭代器对象(生成器对象)

都会先进行调用iter()方法将其变为迭代器,再进行执行next()方法,直到抛出异常

2.1、生成器介绍

为了抽象出迭代器模式,Python2.2(2001年)加入了yield关键字,来构建生成器(generator)。

在调用生成器运行的过程中,每次遇到 yield 关键字时函数会暂停并保存当前所有的运行信息,返回 yield 语句的值, 并在下一次执行 next() 方法时从当前位置继续运行,带有 yield 语句的函数不再是一个普通函数,python 解释器会将其视为一个 生成器(generator),所以只要python函数的定义体中有yield关键字,该函数就是生成器函数。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

def yield_test():
    print('这一行被执行')
    yield 1
    yield 2
 
 
run_test = yield_test()
print(next(run_test))  # 输出:这一行被执行   1
print(next(run_test))  # 输出:2
print(next(run_test))  # 输出:报StopIteration异常
def fibonacci(border):
    a, b = 0, 1
    while True:
        if b < border:
            yield b
            a, b = b, a + b
        else:
            break
 
 
for num in fibonacci(10):
    print(num, end=' ')  # 1 1 2 3 5 8
# 定义生成器函数
def generater_test():
    print('start')
    yield 'A'
    print('continue')
    yield 'B'
    print('end')
 
 
# 列表推导式
gen_list1 = [x*3 for x in generater_test()]
# start
# continue
# end
 
# for循环迭代gen_list1列表
for i in gen_list1:
    print('--->', i)
# ---> AAA
# ---> BBB
 
# 生成器表达式
gen_list2 = (x*3 for x in generater_test())
# gen_list2是一个生成器对象
print(gen_list2)  # <generator object <genexpr> at 0x0000016F37F90EB0>
 
# for循环迭代gen_list2
for j in gen_list2:
    print('--->', j)
# start
# ---> AAA
# continue
# ---> BBB
# end

2.2、 生成器函数

含有 yield 关键字的函数,调用该函数时会返回一个生成器。

可以看到,上面的函数没有使用 return 语句返回值,而是使用了 yield 返回一个值。一个带有 yield 的函数就是一个生成器函数,当我们使用 yield 时,它帮我们自动创建了__iter__() 和 next() 方法,而且在没有数据时,也会抛出 StopIteration 异常,也就是我们不费吹灰之力就获得了一个迭代器,非常简洁和高效。
 

2.3、带有 yield 的函数执行过程 

  1. 调用该函数的时候不会立即执行代码,而是返回了一个生成器对象;
  2. 当使用 next() (在 for 循环中会自动调用 next() ) 作用于返回的生成器对象时,函数 开始执行,在遇到 yield 的时候会『暂停』,并返回当前的迭代值;
  3. 当再次使用 next() 的时候,函数会从原来『暂停』的地方继续执行,直到遇到 yield语 句,如果没有 yield 语句,则抛出异常;
  4. 整个过程看起来就是不断地 执行->中断->执行->中断 的过程。一开始,调用生成器函数的时候,函数不会立即执行,而是返回一个生成器对象;然后,当我们使用 next() 作用于它的时候,它开始执行,遇到 yield 语句的时候,执行被中断,并返回当前的迭代值,要注意的是,此刻会记住中断的位置和所有的变量值,也就是执行时的上下文环境被保留起来;当再次使用 next() 的时候,从原来中断的地方继续执行,直至遇到 yield ,如果没有 yield ,则抛出异常。简而言之,就是 next 使函数执行, yield 使函数暂停。

我们也可以写一个生成器函数或类来实现range函数相同功能:

def my_gen(n):
    i = 0
    while i < n:
        i +=1
        yield i
#这里只是拿到一个生成器函数,并没有调用这个特殊的函数
m = my_gen(10)
#当你执行next(m)时才开始调用这个函数。for循环内部执行next()
#当遇到函数中的yield语句暂停执行,返回yeild语句后的值,再往下继续执行
for i in m:
    print(i)

斐波那契数列实现:

def fab(n):
    num1,num2 = 0,1
    count = 0
    while count<n:
        num1,num2 = num2,num1+num2
        count += 1
        yield num1
f = fab(6)
for i in f:
    print(i)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值