python的迭代器和生成器

1,073 阅读3分钟

本文参考 景霄大神的《python核心技术与实战》

一 、python中的容器、可迭代对象、迭代器的使用


 容器: python中一切皆对象, 对象的抽象就是类, 而对象的集就是容器,所以列表, 元祖,字典等都是容器,并且所有的容器都是可以迭代的(iterable)

  迭代器: 迭代器(iterator)提供了next的方法, 调用这个方法会得到迭代器的下一个对象,直到取完抛出StopIteration错误类型,所以可以通过可迭代对象调取next方法完成遍历迭代器。

可以通过isinstance校验是否为可迭代对象isinstanstance(obj, lterable)

二 、生成器


 通俗的解释是懒人的迭代器, 在使用迭代器构成中所有的元素都会直接一次保存在内存中,但是生成器所有的元素通过调取next动态生成, 直观的感受就是节省了很多资源

 举例:生成一亿的迭代器[for i in range(10000000000)],但是在实际运用中并不会使用到每个元素, 这个时候用生成器替代迭代器会更高效,也并不会报OOM异常, 生成器写法 ( for i in range(100000000))

 作用:迭代器和生成器两者区别还不止内存消耗,迭代器是有穷的,但是因为生成器是动态生成元素,理论上它是可以无限生成下一个元素的,

实例1 计算 (1 + 2 + 3 + ... + n)


def generator(k):
	i = 1
	while True:
		yield i ** k
		i = i + 1

gen = gennerator(1)
def get_sum(n): 
	sum = 0
	for i in range(n):
		_next = next(gen)
		print(_next)
 
 就跟这个公式一样, n可以取无穷, 同样理论上生成器也可以取无穷, 上述代码用yield暂停了函数,并且跳转到next()函数, i ** k 即为next返回值, yield的用法还有很多, 可以代替线程用协程的方法完成函数块的切换,省去了线程间切换消耗的资源,可以用send激活, 这边讲了其中一种用法, 同样它也就是用next激活了生成器。

实例2 在列表中查找某个数字的位置


def func1(list, k):
	out_list = []
	for i, num in enumerate(list):
		if num == k:
			print('fonud it ', i)

def func2(list, k):
	for i, num in enumerate(list):
		if num == k:
			yield i

ps: func2就是生成了一个迭代器对象, 再转list就可以了, 可以让代码更加有可读性。

实例3 查找列表中是否包含子列表


def is_instance(check_list, list):
	list = iter(list)
	
	return all( i in list for i in check_list) # all()函数必须所有返回均为True才会返回True

三、总结

  •  容器是可迭代对象, 可迭代对象调用iter()函数可以得到一个迭代器, 迭代器通过next()得到下一个元素, 从而支持遍历, 注意点: 迭代器遍历只可以使用一次,原因是因为迭代器会存到缓冲区内, 通过移动指针来消耗缓冲区, 随时读取,但是读过一次之后指针就在缓冲区的末尾, 所以不可重复读取。
  •  生成器是一种特殊的迭代器,合理使用生成器可以降低内存占用,优化程序结构,提高程序运行速度
  •  生成器也是在python2中协程的一种重要的实现方式(在python中由于GIL的存在往往使用多进程+协程的方式实现并发), 而在python3.5以上引用了async await语法糖