python中的for、迭代器和生成器的理解
上图的这些模式,都是以list对象和操作符[]来获取元素,而索引只是作为参数。这是一种迭代模式。
有没有一种模式,元素的提取只和下标打交道,而和可迭代对象无关。这样的一种设计模式,就是迭代器模式
for i in [1, 2, 3, 4]:
print(i)
for c in "python":
print(c)
for k in {"x": 1, "y": 2}:
print(k)
for line in open("a.txt"):
print(line, end="")
这些例子中,字符串、列表、字典、文件都可以使用for循环,这些类型都是可迭代对象(字符串、列表、字典、文件)。
迭代器模式特别适合于以下情形:
1、不关心元素的随机访问(不需要干涉遍历的过程,只需要取出来元素)
2、元素的个数不可提前预测(元素个数未知,不像列表、字典、文件那样有固定大小,可以提前加载到内存中)。
那是如何实现迭代器的?
1、返回当前『位置』的元素,当前位置是可迭代对象的起始位置
2、将『位置』向后递增
3、如果到达可迭代对象的末尾,即没有元素可以提取,则抛出StopIteration异常
Python的iter函数和next函数的内部实现:
def iter(obj):
return obj.__iter__()
#Python 2.X
def next(obj):
return obj.next()
#Python 3.X
def next(obj):
return obj.__next__()
iter()和next()方法:
iter()参数是可迭代对象,返回值返回一个迭代器。
next()参数是迭代器,返回值是迭代器中的下一个元素,如果没有下一个元素,则抛出StopIteration异常。
1)可迭代对象包含迭代器。
2)如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
3)定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法。
可迭代对象、迭代器、生成器之间的关系:
结论3与结论2是不是有一点矛盾?既然一个对象拥有了next方法就是迭代器,那为什么迭代器必须同时实现两方法呢?
因为结论1,迭代器也是可迭代对象,因此迭代器必须也实现__iter__方法。
而一个生成器是一个迭代器。
可迭代对象是指:只要具有__iter__或__getitem__的object就是可迭代对象。
而同时具有__iter__和__next__方法的的对象为Iterator迭代器。
而判断一个对象是可迭代对象或者迭代器的方法:
1、利用dir(类型)或者hasattr(类型)去检查是否含有__iter__、__getitem__和__next__这些方法,来判断是一个可迭代对象还是迭代器。
2、isinstance(对象,Iterable),isinstance(对象,Iterator)
例如:
list对象有__iter__以及__getitem__但没有__next__,
所以是list列表是可迭代对象Iterable,但不是迭代器Iterator。
但是,可以通过调用 iter(list)函数,(其实就是call list.__iter__()),
这样会返回一个同时带有__iter__和__next__的新的对象,这个返回值就是一个迭代器Iterator了。
而for循环中 的对象,不需要一定是 迭代器Iterator,只要是可迭代对象就行了。
yield作为及物动词表示产出;作为不及物动词,它表示让步或撤回。python在生成器函数中都体现了这个功能。
1、yield只能出现在function里,而调用带有yield功能的函数会回返回一个Generator生成器对象。
2、Generatoro对象是一个Iterator,带有__iter__和__next__属性。
3、第一次调用next(generator)执行内容等价于将原功能执行到第一次出现yield处,返回yield后的值,并“暂停”执行。
4、第二次之后每次调用next(generator)都会不断从上次「暂停」处继续执行,直到功能全部执行完毕并执行StopIteration。因为yield没有将原功能从调用堆栈中移除,故能暂停并重新回到上次暂停处继续执行。这逻辑也是yield和return最核心不同之处,return会直接将原函数从调用堆栈中移除,终止函数,无论return后面是还是还有其他程序代码。
5、yeild除了可传出值外,也可以接受由外部输入的值,利用generator.send()可以传出和传入值。此设计,让Generator对象可和外部进行双向沟通,可以传出,也可以设定值。
6、关于Generator生成器对象创建的两种语法:一是在function内加入yield”,二是形如x = (i for i in y)的方式。
其实大家常用的产生列表的其中一种写法x = [i for i in range(10)]就是创建一个Generator对象的变形。
迭代器的实现类似于类,其中有两个方法,一个是__iter__,一个是__next__
class iteror:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self): #参数是一个可迭代对象
return self #返回值是一个迭代器
def __next__(self): #参数是一个迭代器
if self.i < self.n:
i = self.i
self.i += 1
return i #返回下一个元素
else:
raise StopIteration() #没有下一个元素,抛出异常
参考文章:https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-6-%E8%BF%AD%E4%BB%A3%E9%82%A3%E4%BB%B6%E5%B0%8F%E4%BA%8B-%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3-iteration-iterable-iterator-iter-getitem-next-fac5b4542cf4