python 迭代对象 迭代器 生成器区别
以上三者和容器分不开。容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。
迭代对象 Iterable
硬性规定就是
- 实现了
__iter__()
方法的就是迭代对象。通过 Iterable 判断是否是迭代对象。
>>> dic = {'a': aa}
>>> from collections import Iterable
>>> print isinstance(dic, Iterable)
True
>>> dir(dic)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values', 'viewitems', 'viewkeys', 'viewvalues']
>>> from collections import Iterator
>>> print isinstance(dic, Iterator)
False
>>>
可以看到确实实现了相应的方法。并且可以看到他不是迭代器。迭代器下面会说到。
迭代器 Iterator
- 实现了
__iter__()
__next__()
返回当前的元素,并指向下一个元素的位置,当前位置已经没有元素的时候,抛出StopIteration异常。
from collections import Iterable
from collections import Iterator
class MyRange(object):
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return val
else:
raise StopIteration()
if __name__ == '__main__':
myRange = MyRange(4)
print type(myRange)
for i in myRange:
print i
print isinstance(myRange, Iterable)
print isinstance(myRange, Iterator)
结果为
<class '__main__.MyRange'>
0
1
2
3
True
True
可以看到实现了这两个方便确实是迭代器了.
- 凡是可作用于for循环的对象都是Iterable类型
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
- 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
生成器
生成器个人的理解有点像把函数当成了对象的概念。使用yield 这个关键字返回函数当前的一个状态值。并且函数内容不会释放掉。这样变量也没有释放。而yield相当于debug中的断点。return了一个值,后面再从这里开始运行,再次停在yield这个关键词上去。而yield关键字还可以通过send来设置当前这个变量的值,相当于从外部传入。
def func(n):
yield n*2
g = func(5)
print 'g type:%s' % type(g)
for i in g:
print i
func 就是一个生成器函数,调用该函数时返回对象就是生成器 g ,这个生成器对象的行为和迭代器是非常相似的,可以用在 for 循环等场景中。注意 yield 对应的值在函数被调用时不会立刻返回,而是调用next方法时(本质上 for 循环也是调用 next 方法)才返回
为什么用生成器取代列表
L = [x * x for x in range(10)]
g = (x * x for x in range(10))
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
不同的是如果range 是10w,则L包含了10万个元素。但是g只是包含了一个元素。当只需要遍历的时候无需记住每一个值是什么,使用g显然更加的合理。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
if __name__ == '__main__':
f = fib(6)
for i in f:
print i
###############
1
1
2
3
5
8
关于send操作暂时接触不多…
廖雪峰 生成器