详细全面解释Python中的迭代器和生成器

迭代器

  • 可以用for循环取值的对象就称为可迭代对象,比如列表、字符串、元组、字典、集合、生成器、迭代器等。
  • 判断一个对象是否是可迭代对象可以使用如下语句:
from collections import Iterable

b = isinstance("abc",Iterable)
print(b)
b = isinstance({},Iterable)
print(b)
b = isinstance([],Iterable)
print(b)
b = isinstance((x for x in range(10)),Iterable)
print(b)
b = isinstance(100,Iterable)
print(b)
  • 运行输出结果如下:
True
True
True
True
False
  • 判断一个对象是否是迭代器可以使用如下语句:
from collections import Iterator

b = isinstance("abc",Iterator)
print(b)
b = isinstance({},Iterator)
print(b)
b = isinstance([],Iterator)
print(b)
b = isinstance((x for x in range(10)),Iterator)
print(b)
  • 运行输出结果如下。从结果可以看出可迭代对象不一定是迭代器,迭代器一定是可迭代对象,且生成器就是迭代器。
False
False
False
True
  • 可以使用iter()将可迭代对象转变为迭代器。
  • 一个类定义了_iter_方法和_next_方法,那么它所生成的对象就是迭代器。
  • 一个类定义了_iter_方法,那么它所生成的对象就是可迭代对象。_iter_方法的返回值必须是实现了_iter_方法和_next_方法的类创建出来的对象引用,也就是返回值必须是迭代器的引用。
  • 下面用for循环来详细说明一下可迭代对象与迭代器:
class Classmate(object):
    def __init__(self):
        self.names = list()

    def add(self,name):
        self.names.append(name)

    def __iter__(self):
        return ClassIterator(self)

class ClassIterator(object):
    def __init__(self,obj):
        self.obj = obj
        self.num = 0

    def __iter__(self):
        pass
    def __next__(self):
        if self.num < len(self.obj.names):
            self.num += 1
            return self.obj.names[self.num-1]
        else:
            raise StopIteration

classmate = Classmate()#Classmate类实现了iter方法,所以它是可迭代对象
classmate.add("老王")
classmate.add("老张")
classmate.add("老虞")

# for循环通常会做几件事情:
# 1. 判断后面的对象是否是可迭代对象
# 2. 调用Python内建函数iter(),将可迭代对象作为参数,该函数将会自动调用可迭代对象类中的__iter__(self)函数,返回一个迭代器。
# 3. 通过内建函数next(),将迭代器作为参数,一直不断的调用迭代器对象类中的__next__(self)函数,直到遇见StopIteration异常。
# 那么在下面这段代码中首先执行的是iter(classmate),得到类ClassIterator的实例对象,然后一只调用next函数,直到条件不满足遇到StopIteration异常。

for cla in classmate:
    print(cla)
  • 以上代码的输出结果如下:
老王
老张
老虞
  • 通常情况下,会将可迭代对象的类和迭代器的类归为一个,所以以上代码还能够以简洁的方式写出:
class Classmate(object):
    def __init__(self):
        self.names = list()
        self.num = 0

    def add(self,name):
        self.names.append(name)

    def __iter__(self):
        return self

    def __next__(self):
        if self.num < len(self.names):
            self.num += 1
            return self.names[self.num-1]
        else:
            raise StopIteration


classmate = Classmate()
classmate.add("老王")
classmate.add("老张")
classmate.add("老虞")

for cla in classmate:
    print(cla)
  • 当然,除了将可迭代对象用于for循环,还能在其他地方体现。比如在将列表转为元组时,其实就是像for循环一样依次调用了iter()和next(),逐个元素进行转换。

生成器

  • 列表生成式能够生成列表,但是如果需要的列表较大,占用的内存空间就会很大,这时就会需要生成器。生成器保存了生成每个元素的计算方式,需要用时再生成某个元素,从而节省了大量的内存空间。其实这也就是使用迭代器的原因。
  • 生成器的第一种创建方式:将列表生成式最外面的中括号改为圆括号。
  • 通过next(generator_object_name)就能够每次让生成器生成一个值。下面举例说明:
>>> a = (x for x in range(10))
>>> a
<generator object <genexpr> at 0x000000886B3898E0>
>>> next(a)
0
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3
  • 生成器的第二种创建方式:包含yield关键字的函数就是生成器,此时调用函数名已经不再是执行函数中的代码而是创建一个生成器对象。通过调用next(generator_object_name)来执行函数中的代码从而每次生成一个值。
  • yield关键字的作用是让函数暂停执行并返回一个值,下一次调用next()方法时就从上次yield暂停的地方继续执行,直到下一次执行yield。下面举例对第二种进行说明:
def fibonacci():
    a, b = 0, 1
    for i in range(10):
        print("----1----")
        yield b
        print("----2----")
        a, b = b, a+b
        print("----3----")


generator = fibonacci()
for x in range(3):
    v = next(generator)
    #还有一个等价的写法如下:
    #v = a.__next__()
    print(v)
  • 运行上面代码输出内容如下。从输出结果也能够看出每次执行至yield关键字都会暂停,下次会从上次暂停的地方继续执行。
----1----
1
----2----
----3----
----1----
1
----2----
----3----
----1----
2
  • 生成器也可以用for循环进行迭代,且for循环会自动停止(不像使用next()在最后不能继续生成时会报错):
def fibonacci():
    a, b = 0, 1
    for i in range(10):
        print("----1----")
        yield b
        print("----2----")
        a, b = b, a+b
        print("----3----")


generator = fibonacci()
# 每次for循环都会从上次yield暂停的地方继续执行,直到遇见StopIteration异常。
for x in generator:
    print(x)
  • 输出结果如下:
----1----
1
----2----
----3----
----1----
1
----2----
----3----
----1----
2
----2----
----3----
----1----
3
----2----
----3----
----1----
5
----2----
----3----
  • 生成器对象除了__next__()方法之外还有一个send()方法也能实现让生成器继续向下走一步的功能,它们的区别在于send()方法可以传入一个参数用来当做yield variable语句的返回值。下面用代码来进行解释:
def test():
    for i in range(5):
        print("----1----")
        temp = yield i
        print("----2----")
        print(temp)
        print("----3----")


t = test()
a = t.__next__()
print(a)
b = t.send("hello")
print(b)
  • 以上代码的执行结果如下。从输出结果可以看出send()语句执行是从第四行代码的赋值语句开始的,首先将传入的参数赋值给temp变量,然后再继续执行至下一次yield语句。
----1----
0
----2----
hello
----3----
----1----
1
  • 总结一下,生成器是一类特殊的迭代器,迭代器能够保存生成数据的方式,节省内存空间。而生成器最大的特点是用了yield,是函数能够暂停执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值