在初次接触python的时候,我们有时会遇到 for i in [1, 2, 3, 5, 8]: print(i)这样类似的语句,比起java和c++的for(int i=0; i<n; i++) printf("%d\n", a[i])这样的语句,简洁了许多。
在python中,一切接对象,对象的抽象就是类,对象的集合就是容器。
列表、元组、字典、集合都是容器。所有的容器都是可迭代的,这里的迭代,和枚举不同。迭代时,你并不需要知道里面有多少元素。
迭代器提供了一个next方法,调用这个方法后,得到容器的下一个对象,迭代完后,会报StopIteration错误。可迭代对象,通过iter()函数返回一个迭代器,再通过next() 可实现遍历,for in 语句将这个过程隐式化。
可迭代类型:
- 字符串
- 列表
- 元组
- 集合
- 字典
那么,生成器又是什么?
通俗理解,生成器是懒人版本的迭代器,它的好处有许多,请看如下示例:
import os
import psutil
#显示当前python程序占用内存大小
def show_memory_info(hint):
pid = os.getpid()
p = psutil.Process(pid)
info = p.memory_full_info()
memory = info.uss/1024./1024
print('{}memory used:{} MB'.format(hint,memory))
def test_iteration():
show_memory_info('initing iterator')
list1 = [i for i in range(10000000)]
show_memory_info('after iterator initiated')
print(sum(list1))
show_memory_info('after sum called')
def test_generation():
show_memory_info('initing iterator')
list2 = (i for i in range(10000000))
show_memory_info('after iterator initiated')
print(sum(list2))
show_memory_info('after sum called')
test_iteration()
test_generation()
使用迭代器很简单,如果迭代器中的元素太多,内存不够的话会报OOM错误。
生成器便不一样了,它并不会像迭代器一样占用巨量内存,它只有在被使用时才会被调用,在你调用next()时才会生成下一个变量。元素非常多时,生成器所用的时间和内存都比迭代器少上许多。
下面举一个例子:
给定一个指定数字,求这个数字在list中的位置
常规做法:
def find_index(alist, target):
result = []
for i, num in enumerate(alist):
if num == target:
result.append(i)
return result
print(find_index([3, 2, 56, 2, 6, 35, 2, 63], 2))
#>>>[1, 3, 6]
上面是常规做法,枚举。下面使用迭代器和生成器,代码如下:
def index_generator(alist, target):
for i, num in enumerate(alist):
if num == target:
yield i
print(list(index_generator([3, 2, 56, 2, 6, 35, 2, 63], 2))) #必须转换成列表后才能输出结果
#>>> [1, 3, 6]
python讲究的是用更少更清晰的代码实现相同功能,生成器便能很好符合这个要求。
再来个例子:
给定两个序列,判断第一个是不是第二个的子序列,字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。
这个问题的常规解法为贪心算法,使用两个指针指向列表的开始,对第二个序列一路扫过去,如果某个数字和第一个指针一样,就把第一个指针前进一步,如此判断。
如果使用迭代器和生成器呢?
def judge(a, b):
b = iter(b)
return all(i in b for i in a)
print(judge([1,3,2], [1,2,3,4,5]))
print(judge([1,4,5], [1,2,3,4,5]))
# True
# False
总结:
- 容器是可迭代对象,迭代器可以通过next()函数来得到下一个元素,从而支持遍历
- 生成器是一种特殊的迭代器,合理使用生成器,可以降低内存占用、优化程序结构、提高运行速度
- 生成器是实现协程的一种重要方式。