目录
1 可迭代对象
1.1什么是可迭代对象、如何判断是否是可迭代对象
在Python中,我们把所有可以迭代的对象统称为可迭代对象,有一个类专门与之对应:Iterable。大多数容器也是可迭代的。但还有很多东西是可迭代的。例如打开的文件、打开的套接字等等。在容器通常是有限的情况下,可迭代可以表示无限的数据流。
可迭代对象是可以返回迭代器(目的是返回其所有元素)的任何对象,不一定是数据结构。这听起来有点尴尬,但是可迭代对象和迭代器之间存在重要区别。
>>> from collections import Iterable
>>> isinstance(123, Iterable)
False
>>> isinstance(True, Iterable)
False
>>> isinstance('abc', Iterable)
True
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance((), Iterable)
True
怎么让一个对象可迭代呢?毕竟,很多时候,我们需要用到的对象不止Python内置的这些数据类型,还有自定义的数据类型。答案就是实现__iter__()方法,只要一个对象定义了__iter__()方法,那么它就是可迭代对象。
from collections.abc import Iterable
class A():
def __iter__(self):
pass
print('A()是可迭代对象吗:',isinstance(A(),Iterable))
//结果输出为:
//A()是可迭代对象吗: True
只要一个对象定义了__iter__()方法,那么它就是可迭代对象。
2 迭代器
迭代器是对可迭代对象的改造升级,上面说过,一个对象定义了__iter__()方法,那么它就是可迭代对象,进一步地,如果一个对象同时实现了__iter__()和__next__()方法,那么它就是迭代器。
在Python中,也有一个类与迭代器对应:Iterator。所以,要判断一个类是否是迭代器,只要判断是否是Iterator类的实例即可。
from collections.abc import Iterable
from collections.abc import Iterator
class B():
def __iter__(self):
pass
def __next__(self):
pass
print('B()是可迭代对象吗:',isinstance(B(), Iterable))
print('B()是迭代器吗:',isinstance(B(), Iterator))
/*
结果输出如下:
B()是可迭代对象吗: True
B()是迭代器吗: True
*/
3 for循环的本质
x = [1, 2, 3]
x = iter(x)
try:
while True:
print(next(x))
except StopIteration:
pass
从上我们可以看出,for循环的本质是先将可迭代对象列表x利用iter(x)调用__iter__()方法,返回迭代器。然后通过next(x)可以调用魔法函数__next__(),实现下一个元素的获取。
4 生成器
终于,我们到达了目的地!生成器是我最喜欢的Python语言特性。生成器是一种特殊的迭代器,优雅的迭代器。
生成器使您可以像上面的Fibonacci序列迭代器示例一样编写迭代器,但是使用简洁的语法,避免使用__iter __()和__next __()方法编写类。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
>>> fib(6)
1
1
2
3
5
8
'done'
仔细观察,可以看出,fib
函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。也就是说,上面的函数和generator仅一步之遥。要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。