引言

今天在阅读ptan(开源强化学习算法库)源码的时候,发现其中有很对应用yield和iter等方法和关键字的地方,因此专门写个博客对于其中的这些名词和用法进行讲解。

可迭代对象和迭代器

可迭代对象

1、什么是可迭代对象,直观来讲,一个实例或者函数,数据结构等可以成为可迭代对象,是因为其在内部实现了__iter__()方法。可迭代对象可以使用for访问,但是不能用next访问。

2、当一个对象仅实现了__iter__()方法时,其__iter__()的返回值必须是一个迭代器。

3、当使用iter()函数作用到一个可迭代对象得时候,其作用是将可迭代对象变成迭代器,即可以使用for循环或者next方法进行迭代。

通过自己编写的程序检验,注:python3.6中使用__next__()代替next():

class test_a(object):
    def __init__(self,data):
        self.data = data
    def __iter__(self):
        return test_b(self.data)
class test_b(object):
    def __init__(self,data):
        self.data = data
    def __iter__(self):
        return self
    def __next__(self):
        while(True):
            self.data = self.data + 1
            return self.data
            print(self.data)
a = test_a(1)
b = iter(a)
print(next(b))
print(next(b))

1、为了与接下来讲道德yield区分,在return后面又加了一个print,从输出可以看到,不像yield,这个return后的函数并没有执行。

2、在test_a中实现了可迭代对象,在test_b中实现了迭代器。

输出如下:

当我们不使用iter(),即使用对象a继续next操作时:

但是仍然可以使用for循环访问:

迭代器

1、迭代器就是在可迭代对象的基础上,又实现了next()方法的对象。

还是以上面的例子介绍,我们可以直接定义迭代器对象,并使用next方法进行访问:

yield

用法

def fun():
    a = 1
    while True:
        res = yield a
        print(res)
a = fun()
print(next(a))
print(next(a))
print(a.send(7))

上面的这个函数在执行时

1)当执行到第一个print(next(a))时,程序会在执行到res = yield a后暂停。直到第二个print(next(a))函数出现,第二个print(next(a))函数出现后,将继续从fun中暂停的代码处开始继续执行。

2)当执行到fun中的print时,由于res的值已经被yield走,以此返回None。

3)第三个a.send(7)的作用是,为yield所在的res赋值,程序将从赋值后再执行print(res)。

最终输出为:

注意,yield等迭代器经常与while搭配使用

定位

yield准确来说,是一个生成器,一个对象是生成器,那他一定也是迭代器。当使用yield时,不需要在内部显式得实现__iter__()方法和next()方法。

由于其可以i作为迭代器,那么一定可以用来对可迭代对象创建迭代器。

以ptan库中代码为例:

在DQN的reply buffer函数的设置中,首先将experience_source这个可迭代对象使用iter,将其变成迭代器:

在增加一条样本的函数中,使用迭代器的next方法。

那我们来看下,在这个可迭代对象类的__iter__()方法中是怎么定义的吧:

对,使用的就是yield这个生成器。

至此,关于生成器,迭代器,可迭代对象就讲完了,有不正确的地方请不吝指教!

May the force be with you!