在 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 很多应用场景和技术延伸都和生成器有关。
怎么实现一个生成器
生成器是一种特殊的迭代器,但是生成器比迭代器更好实现,实现一个生成器一般有两种方式:列表生成器
、函数生成器
。
- 列表生成器
列表生成器是在列表生成器的基础上稍微变动即可。如下是一个普通的列表生成式:
new_list = [x*2 for x in range(5)]
而列表生成器只需要将两边的[ ]
改成( )
即可。如下是一个列表生成式:
new_generator = (x*2 for x in range(5))
- 函数式生成器
使用函数来实现一个生成器也非常简单,只需要使用yield
关键字,而不需要关注next
、StopIteration
异常,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)
生成器有什么用?
- 生成器简化了迭代器的实现过程,例如我们想要
网友评论