一文搞懂容器、迭代器和可迭代对象、生成器
容器(container)
简单来说,容器是存储某些元素的统称,最大的特性就是判断一个元素是否在这个容器内。
一个类只要实现了__contains__()
方法,那么它就是一个容器,也就是说,如果想自定义一个容器,则在类中定义一个__contains__()
方法即可:
# con is an container
class con(object):
def __init__(self):
self.items = [1,2]
def __contains__(self, item):
return item in self.items
迭代器(iterator)
一个类如果实现了[迭代器协议],我们就说它是一个迭代器。
所谓实现[迭代器协议],在python中意味着以下2点:
1. 实现了__iter__()
方法,返回对象本身
2. 实现了__next__()
方法,返回迭代的值,如没有可迭代元素,则抛出StopIteration
异常
比如,我们可以定义一个迭代器ite:
# ite is an iterator
class ite(object):
def __init__(self):
self.n = 0
def __iter__(self):
return self
def __next__(self):
if self.n < 100:
self.n += 1
return self.n
else:
raise StopIteration()
if __name__ == "__main__":
a = ite()
for i in a:
print(i)
迭代器必须同时内置__iter__()
有__next__()
函数,__iter__()
返回迭代器自身,__next__()
返回迭代的下个值,迭代器类似于工厂模式,每次返回一个值,当迭代器没有返回的元素,则抛出StopIteration。
迭代器的优点:不论能迭代出多大的数据量,只占用非常少的内存,Django中的Queryset
就是这个原理。
可迭代对象(Iterable)
从语法形式上讲,能调用__iter__()
方法的数据对象就是可迭代对象。
可迭代对象内置了__iter__()
函数,该函数将对象处理为可迭代对象,调用后返回迭代器。
列表,元组,字典,集合,字符串都是可迭代对象。
迭代器必须是可迭代对象,但可迭代对象不一定是迭代器。
list
,tuple
,dict
,set
等只是可迭代对象,而不是迭代器。
生成器(generator)
生成器是一个特殊的迭代器,它自动实现了迭代器协议,不需要手动维护__iter__()
和__next__()
这两种方法。
有2种方式可以创建一个生成器:
1. 生成器表达式,(expression for item in iterable if condition)
, 返回一个生成器对象
# gen is a generator
gen = (i for i in range(100))
2. 生成器函数,只要在函数中声明一个`yiled`关键字即可
# gen is a generator
def gen(n):
for i in range(100):
yield i
这里说一下yield
关键字,它最了不起的能力就在于暂停和恢复的功能。
yield
和return
的区别:
包含 return
的方法会以 return
关键字为最终返回,每次执行都返回相同的结果retrun
执行后,函数结束;
包含 yield
的方法一般用于迭代,每次执行时遇到 yield
就返回 yield
后的结果,但内部会保留上次执行的状态,下次继续迭代时,会继续执行yield
之后的代码,直到再次遇到yield
后返回,也就是说,yield
只是将函数的状态挂起。
生成器对象比迭代器多几个方法,如:__close__()
,__send__()
最后我们可以用一张图总结容器、可迭代对象和迭代器,以及生成器之间的关系: