迭代器
迭代是访问集合元素的一种方式,迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完为止。迭代器只能往前不会后退
可迭代对象
- 我们把可以通过for…in…这类语句迭代读取一条数据供我们使用的对象称为可迭代对象(Iterable)
- 如何判断一个对象是否是可迭代对象
- 导包:from collections import Iterable
- 判断:isinstance(对象, Iterable)
- 可迭代对象的本质
- 在for…in…中每循环一次都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中应该有一个”人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的”人”称为迭代器
- 可迭代对象的本质就是可以向我们提供一个这样的中间”人”即迭代器帮助我们对其进行迭代遍历使用
- 可迭代对象通过iter方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据
- 那么也就是说,一个具备了iter方法的对象,就是一个可迭代对象
迭代器
- list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器,然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据
- iter()函数实际上就是调用了可迭代对象的iter方法
- 注意:当我们已经迭代完最后一个数据之后,再次调用next()函数会抛出StopIteration异常,来告诉我们所有数据都已迭代完成
- 如何判断一个对象是否是迭代器:可以使用isinstance()判断一个对象是否是Iterator对象
- 迭代器的本质
- 迭代器可以帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据
- 实际上,在使用next()函数的时候,调用的就是迭代器对象的next方法。所以我们要想构造一个迭代器,就需要实现它的next方法
- 但这还不够,python要求迭代器本身也是可迭代的,所以还需要实现迭代器的iter方法,而iter方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的iter方法返回自身即可
- 因此,一个实现了iter方法和next方法的对象,就是迭代器
- for item in Iterable循环的本质:就是先通过iter()函数获取可迭代对象的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后结束循环
- 迭代器的应用场景:迭代器最核心的功能就是可以通过next()函数的调用来返回下一个数据值。如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定规律计算生成的,那么也就意味着可以不用再依赖一个已有的数据集合,也就是说不用再将所有要迭代的数据都一次性缓存下来供后续依次读取,这样就可以节省大量的存储空间 ```python from collections.abc import Iterable, Iterator
自定义一个迭代器
class MyList(object): def init(self): self.container = [] self.current = 0 # current用来记录当前访问到的位置
def add(self, item):
self.container.append(item)
def __next__(self):
if self.current < len(self.container):
item = self.container[self.current]
self.current += 1
return item
else:
raise StopIteration
def __iter__(self):
return self
if name == ‘main‘: my_list = MyList() my_list.add(1) my_list.add(2) my_list.add(3) print(isinstance(my_list, Iterable)) # True print(isinstance(my_list, Iterator)) # True print(next(my_list)) # 1 for num in my_list.container: print(num) # 1 2 3
```python
class Fibonacci(object):
def __init__(self, n): # n表示斐波那契数列中的元素数量
self.n = n
self.current = 0
self.num1 = 0
self.num2 = 1
def __next__(self): # 被next()函数调用来获取下一个数
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1 + self.num2
self.current += 1
return num
else:
raise StopIteration
def __iter__(self):
return self
if __name__ == '__main__':
fibnacci = Fibonacci(10)
for i in fibnacci:
print(i, end=" ") # 0 1 1 2 3 5 8 13 21 34
生成器
利用迭代器,我们可以在每次迭代获取数据时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器
创建生成器
- 方法一:只要把一个列表生成式的 [ ] 改成 ( )
```python
L = [x * 2 for x in range(3)]
print(type(L)) #
print(L) # [0, 2, 4] next(L) # TypeError: ‘list’ object is not an iterator
for i in L: print(i) # 0 2 4
G = (x * 2 for x in range(3))
print(type(G)) #
print(next(G)) # 0
for i in G: print(i) # 0 2 4
- 方法二:在函数中实现基本逻辑,并用yield返回值
- 简单来说,只要在函数中有yield关键字的就称为生成器,而不再是函数
- 此时,按照调用函数的方式就不再是执行函数体,而是返回一个生成器对象,然后就可以按照使用迭代器的方式来使用生成器
- yield关键字的作用
- 保存当前运行状态,然后暂停执行,即将生成器挂起
- 将yield关键字后面表达式的值作为返回值返回,可以理解为起到了return的作用
- 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器
```python
def fibonacci(n):
current = 0
num1, num2 = 0, 1
while current < n:
num = num1
num1, num2 = num2, num1 + num2
current += 1
yield num
F = fibonacci(5)
print(type(F)) # <class 'generator'>
for i in F:
print(i) # 0 1 1 2 3
使用send唤醒
我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据
def gen():
i = 0
while i < 5:
temp = yield i
print(temp)
i += 1
g = gen()
print(next(g)) # 0
print(next(g)) # None 1
print(g.send('hello')) # hello 2
print(g.send('world')) # world 3