美文网首页
可迭代对象(Iterable)、迭代器(Iterator)、生成

可迭代对象(Iterable)、迭代器(Iterator)、生成

作者: eeert2 | 来源:发表于2019-08-03 00:16 被阅读0次

在 python 中,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

1 迭代器 与 可迭代对象

  • 迭代器协议:
    对象必须提供一个__next__()方法,执行方法要么返回迭代器中的下一项,要么就引起一个StopIteration异常,以终止迭代(只能往后走,不能往前退)

  • 迭代器:
    实现了迭代器协议的对象称为迭代器。

  • 可迭代对象
    实现一个__iter__()方法,并返回一个迭代器的对象称为可迭代对象。

  • 迭代器与可迭代对象
    1)迭代器是可迭代对象的 功能封装,类似于可迭代对象的一个属性,但是绕了一下,通过__iter__()方法来返回一个迭代器(后面有讲解为什么要绕一下)。
    2)从实现难度上来看,可迭代对象比迭代器更容易实现(本人自认为),可迭代对象只要包含一个迭代器就可以;而实现一个迭代器则要关注迭代器的__next__() 以及StopIteration异常,有点复杂了...

例:实现一个自定义的可迭代对象 School。

class Student:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

class School:
    def __init__(self):
        self._students = []

    def add_student(self, student):
        self._students.append(student)
    
    def __iter__(self):
        # 这里需要返回一个迭代器,我们使用内置的iter()方法取出列表的迭代器
        # iter(self._students) 与 self._students.__iter__()效果是一致的
        return iter(self._students)

if __name__ == '__main__':
    school = School()
    student1 = Student('lilei')
    student2 = Student('hanmeimei')
    school.add_student(student1)
    school.add_student(student2)
    # 对可迭代对象进行迭代
    for  stud in school:
        print(stud)

python内置的list、tuple、dict、set、str、file文件对象等都是可迭代对象,它们包含了迭代器。使用内置的item() 可以取出可迭代对象的迭代器。

这里的 iter() 函数的使用简化了代码, iter(s) 只是简单的通过调用 s.iter() 方法来返回对应的迭代器对象, 就跟 len(s) 会调用 s.len() 原理是一样的。

3)如果仔细考虑,你一定会有一个疑问,如果只关注迭代功能的话,只需要迭代器就可以了,为什么还要将迭代器封装到可迭代对象中呢?
3.1)一个原因是可迭代对象可以封装更多的功能,不仅仅是迭代,例如 列表、元组等,除了在 for 循环的时候调用 __iter__(), 它们还有很多其它的功能函数。
3.2)更重要的一个原因是迭代器是有尽头的,一个迭代器对象只能进行一次迭代。如下

weeks = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
# 取出可迭代对象的迭代器
weeks_iterator = iter(weeks)

# 直接对迭代器进行迭代
for day in weeks_iterator:
    print(day)

for day in weeks_iterator:
    print(day)

上面对同一个迭代器进行了两次迭代,但实际只会打印一次,因为在第一次迭代过后就已经 耗尽迭代器的内容,后面再进行迭代__next__()只会引起StopIteration异常

要实现对迭代器进行多次迭代,则需要多个迭代器对象,或者每次迭代的迭代器都是新的,

class School:
    
    def __iter__(self):
        # 使用__iter__() 都会返回一个新的迭代器
        return iter(self._students)

可迭代对象协议 要求实现 __iter__()并返回迭代器,而不是直接将迭代器封装成 对象属性self.iterator = iter(self._students)也是基于这个原因,每次进行 for 循环,可迭代对象都会执行一次 __iter__() 取出一个新的迭代器。

2 生成器

首先我要说的是,生成器很重要,迭代器可以不了解,只要会用 for 循环就行,但是生成器确必须深入了解,python 很多应用场景和技术延伸都和生成器有关。

怎么实现一个生成器

生成器是一种特殊的迭代器,但是生成器比迭代器更好实现,实现一个生成器一般有两种方式:列表生成器函数生成器

  1. 列表生成器
    列表生成器是在列表生成器的基础上稍微变动即可。如下是一个普通的列表生成式:
new_list = [x*2 for x in range(5)]

而列表生成器只需要将两边的[ ]改成( )即可。如下是一个列表生成式:

new_generator = (x*2 for x in range(5))
  1. 函数式生成器
    使用函数来实现一个生成器也非常简单,只需要使用 yield 关键字,而不需要关注 nextStopIteration异常,yield的内容就是每次访问生成器得到的对象。如下:
def my_generator(stop):
    n = 0
    while True:
        n += 1
        if n == stop:
            break
        yield n

使用生成器

for i in my_generator(10):
    print(i)

生成器有什么用?

  1. 生成器简化了迭代器的实现过程,例如我们想要

相关文章

网友评论

      本文标题:可迭代对象(Iterable)、迭代器(Iterator)、生成

      本文链接:https://www.haomeiwen.com/subject/dflddctx.html