迭代:访问集合元素的一种方式
迭代器:可以记录当前迭代位置的对象
可迭代对象:可以通过for ... in ...之类的语句依次获取其中一条数据的对象
可迭代对象
如下代码中,isinstance函数可以判断一个对象是否为可迭代对象
from collections import Iterable
print(isinstance([], Iterable))
# True
也可以用我们常用的方法来判断常用的数据类型是否为可迭代对象
def test(para):
try:
for i in para:
pass
print(type(para), "is iterable")
except TypeError as e:
print(type(para), e)
test([1, 2, 3])
test((1, 2, 3))
test({"a": 1, "b": 2, "c": 3})
test({123})
test("123")
test(123)
# <class 'list'> is iterable
# <class 'tuple'> is iterable
# <class 'dict'> is iterable
# <class 'set'> is iterable
# <class 'str'> is iterable
# <class 'int'> 'int' object is not iterable
由上可知,Python的六种标准数据类型中,字符串、列表、元组、字典、集合都是可迭代对象,而数字不是可迭代对象
理解可迭代对象
可迭代对象通过其__iter__()方法返回一个迭代器,我们可以使用iter()函数来获取迭代器,然后不断使用next()函数来获取对象里的值
a = [1, 2, 3]
a_iterable = iter(a)
print(next(a_iterable))
print(next(a_iterable))
print(next(a_iterable))
# 1
# 2
# 3
迭代器
由上描述可知,可迭代对象的核心是迭代器。迭代器是用来帮我们记录每次迭代访问到的位置,当使用next()函数时,迭代器会返回下一个位置的数据(在处理大量数据时不用把所有数据都读到内存中,节省CPU和内存性能)
所以,一个实现了__iter__()方法和__next__()的对象就是就是迭代器
class MyList:
# 可迭代对象
def __init__(self):
self.items = list()
def add(self, value):
self.items.append(value)
def __iter__(self):
return MyIterator(self)
class MyIterator:
# 迭代器
def __init__(self, my_list):
self.my_list = my_list
self.current = 0
def __next__(self):
if self.current < len(self.my_list.items):
item = self.my_list.items[self.current]
self.current += 1
return item
else:
raise StopIteration
def __iter__(self):
return self
if __name__ == '__main__':
test = MyList()
test.add(1)
test.add(2)
test.add(3)
for i in test:
print(i)
如上程序自定义一个可迭代对象MyList,定义一个供MyList调用的迭代器MyIterator,简单的自定义实现方法仅供参考。
理解 for ... in ... 语句
由上代码最后两行可以看出,for ... in ... 语句其实就是先获取可迭代对象的迭代器,不断的对迭代器进行next()调用,直到遇到StopIteration异常才结束
生成器
生成器是一种特殊的迭代器
生成器和迭代器都可以通过next()方法,将数据迭代生成。但是生成器语法更简单、易懂
简单的生成器
如下变量a是列表,b却是一个生成器,我们可以通过b.__next__()或者next(b)来获取生成器里的值。
a = [i for i in range(3)]
b = (i for i in range(3))
print(a)
print(b)
while True:
try:
print(next(b))
except StopIteration:
break
# [0, 1, 2]
# <generator object <genexpr> at 0x00000000006447C8>
# 0
# 1
# 2
生成器比较强大,有时列表生成式无法完成的或者实现比较困难的数列可以用生成器完成,斐波那契数列就是经典的用法
def fib(n):
current = 0
a, b = 0, 1
while current < n:
yield a
a, b = b, a + b
current += 1
F = fib(3)
print(type(F))
print(next(F))
print(next(F))
print(next(F))
# <class 'generator'>
# 0
# 1
# 1
生成器的逻辑很像迭代器中__next__()方法,只是将关键字return改为yield。
yield关键字保存当前运行状态,将生成器挂起,返回关键字后的表达式。理解可以参考线程的yielding
生成器除了可以用next()方法唤醒执行,还可以用send()方法,唤醒的同时传入参数
def num():
a = 1
para = 0
while True:
para = yield a + para
n = num()
print(type(n))
print(next(n))
print(n.send(1))
print(n.send(2))
# <class 'generator'>
# 1
# 2
# 3
总结篇:
可迭代对象:更贴近我们写的程序,数据结构 + 算法 = 程序,通常对象(万物皆对象)是我们直接调用操作的目标
迭代器和生成器的共性:都是通过next()方法,按照一定的规律,在具体需要(执行)的时候返回的数据的对象
迭代器和生成器的不同:
- 形式:迭代器需要实现__iter__()和__next__(),而生成器在函数定义中使用yield关键字即可实现
- 内容:迭代器我们操作的是对象(如实例对象),获得的是数据。生长器我们操作的是生成器,返回的也是(携带数据的)生长器