Iterables
python中可以对其内部进行遍历的类型都是iterable。从源码解读,任何一个包含返回值为迭代器(iterator)的__iter__
方法的对象,和包含支持下标检索的__getitem__
方法的对象都是一个iterable。
>>> my_list = [i for i in range(1000)]
>>> hasattr(my_list '__iter__')
True
>>> hasattr(my_list, '__getitem__')
True
或者我们可以利用python在typing
库中定义的Iterable来进行判断:
>>> from typing import Iterable
>>> my_list = [i for i in range(1000)]
>>> isinstance(my_list, Iterable)
True
或者我们可以采用另一种简单的判断方式,只要我们可以对一个对象执行for ... in ...
的操作,那么它就是一个iterable。
常见的iterable包括list,dict,str,set, tuple等。
Iterator
Iterator就是一个定义了__next__
方法的对象,__next__
和__iter__
方法构成了迭代器协议,前者返回下一个元素,后者返回迭代器自身。
Generator
上面提到的iterables非常常见且常用,因为我们可以存储和获取任意数量的元素,但同时也带来一个问题,当我们拥有大量的元素时,我们不希望为了获取其中一个元素就把整个iterablr读到内存中,所以这时候我们需要用到生成器(generator)。
假设我们要存放1到10000这10000个数字,如果采用列表,那么我们需要10000个地址空间。但如果利用生成器,那么就只需要一个地址空间,因为生成器直到运行时才会生成值写进内存,此时前一个值就被抛弃。当然内存的节省必然有其代价,生成器内的元素只能进行一次遍历。
生成器的创建类似list,只不过将[]
替换为()
:
my_generator = (i for i in range(10000))
或者我们可以利用yield关键字来将一个函数定义为generator。
Yield
yield的用法类似于return的关键字,但不同的是带有yield的函数就变成了一个generator,每次执行到yield的时候返回一个生成的对象并保存当前运行状态,直到下一次调用next()
继续运行下一轮迭代。
一个简单的例子:
def num_generator(num: int):
for i in range(num):
print('当前的i =', i)
yield i
print('完成第', i, '次运行\n')
iter = num_generator(3)
for i in iter:
print(i)
print('完成第', i, '次yield')
# Result:
# 当前的i = 0
# 0
# 完成第 0 次yield
# 完成第 0 次运行
# 当前的i = 1
# 1
# 完成第 1 次yield
# 完成第 1 次运行
# 当前的i = 2
# 2
# 完成第 2 次yield
# 完成第 2 次运行
#
参考:
https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do?page=1&tab=votes#tab-top
https://zhuanlan.zhihu.com/p/46259258