本文参考 景霄大神的《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语法糖