# -*- coding:utf-8 -*-
# 迭代---------
# 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历称为迭代
# 在python中,迭代通过for...in完成,而很多语言比如c或者Java,迭代通过下标完成,如Java代码

'''
for (i = 0,i at 0x000000000499D438>

'''
创建L和g的区别仅在于自外层的[]和(),L是list,g是generator,我们可以直接打印出list的每一个元素,
而g是一个generator。可以直接打印出list的每一个元素,用next()函数获得generator的下一个返回值,
'''
print next(g); #1
print next(g); #4
print next(g); #9

#可以用for循环,generator可迭代
g = (x * x for x in xrange(1,5));
for n in g:
    print (n);  # 1,4,9,16
'''
所以当创建了一个generator后,基本上不会用next()而是用for来迭代他,并且不需要关心StopIteration
错误。   
generator非常强大,如果推算的算法比较复杂,用类似的列表生成的for循环无法实现的时候,还可以用函数实现
如,著名的斐波拉契数列fibonacci,除第一个和第二个数外,任意一个数都可以由前两个数相加得到1,1,2,3,5,8...
用列表生成式写不出来,但是,用函数很容易
'''

def fib(x):
    n,a,b = 0,0,1;
    while n
# 这是定义generator的另一个方法,如果一个函数定义中包含yield关键字,那么这个函数就不是普通函数,
# 而是generator

'''
这里难理解的就是generator和函数的执行流程不一样,函数是顺序执行,遇到return语句或者最后一行函数
就返回,而变成generator函数,在每次调用next的时候执行,遇到yield语句返回,再次执行是从上次返回的
yield处继续执行.
'''

# 举一个简单的例子,定义一个generator,依次返回数字1.3.5
def odd():
    print('step 1');
    yield 1;
    print('step 2');
    yield 3;
    print ('step 3');
    yield 5;
o = odd()
print next(o);
'''
step 1
1
'''
print next(o);
'''
step 2
3
'''
print next(o);
'''
step 3
5
可以看到,odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,执行3次后没有yield可执行
了就会报错
回到fib例子,在循环过程中不断调用yield,就会不断中断,当然要给循环设置一个条件来退出循环,不然就会产生
无线数列
同样的,把函数改成generator后,基本上不会用next来获取下一个返回值,而是用for迭代
'''

for n in fib(5):
    print (n); # 1 1 2 3 5

'''
但是用for循环调用generator时,发现拿不到generator的return语句的返回值,必须捕获stopIteration
错误,返回值包含在stopIteration的value中
'''
g = fib(5);
while True:
    try:
        x = next(g);
        print('g:'),x;
    except StopIteration as e:
        print('Generator return value:',e);
        break;
'''
g: 1
g: 1
g: 2
g: 3
g: 5
('Generator return value:', StopIteration())
'''



#----------------------------------------------------------
#----迭代器
'''
可以直接作用于for循环的数据类型有,一类为集合数据类型(list tuple dict str..
一类是generator,包括生成器和带yield的generator function
这些能直接作用于for循环的对象统称为可迭代对象Iterable,可以用isinstance()判断是否可迭代
生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,知道最后抛出StopIteration
可以被next调用并不断返回下一个值的对象叫迭代器:Iterator
生成器都是Iterator 对象,但list dict str 虽然是Iterable,却不是Iterator
用isinstance()判断一个对象是否是Iterator对象
把list dict str 等iterable(可迭代对象)变成Iterator(迭代器)用ITER()函数

'''
from collections import Iterator;
print isinstance((x for x in range(5)),Iterator); #True
print isinstance([],Iterator); #False
print isinstance({},Iterator); #False
print isinstance('abcd',Iterator); #False

print isinstance(iter([]),Iterator); #True
print isinstance(iter('azx'),Iterator); #True

'''
为什么list dict str等数据类型不是Iterator
因为python的Iterator对象表示一个数据流,Iterator对象可以被next函数调用并不断返回下一个数据,直到
没数据抛出StopIterateration错误,可以把这个数据流看成是一个有序的序列,但我们却不知道序列的长度,只能
通过不断调用next函数实现按需计算下一个数据,所以Iterator得计算是有惰性得,只有在需要返回下一个数据
时他才会计算
Iterator甚至可表示一个无线大得数据流,例如全体自然数,然而使用list是永远不可能存储全体自然数的
凡是可用于for循环的对象都是Iterator类型
凡是可作用于next函数的对象都是Iterator类型,他们表示一个惰性计算的序列
集合数据类型list dict str等是Iterable但不是Iterator,可通过iter()函数获得一个Iterator对象
python的for循环本质就是不断调用next函数实现
'''




#---------------------------------------------------------------------------
#--补充
# -----------------------------------------------------------------------
#-----迭代器
# 迭代器协议,仅需要__iter__()和next()两个方法,前者返回迭代器对象,后者依次返回数值,直到引发
# stopIteration异常结束
# 最简单的做法是用内置函数iter(),它返回常用类型的迭代器包装对象,问题是,序列类型已经可以被for处理
# 为什么还要这么做?

class Data(object):
   def __init__(self):
      self._data = []
   def add(self, x):
       self._data.append(x)
   def data(self):
       return iter(self._data)
d = Data();
d.add(1)
d.add(2)
d.add(3)
for x in d.data():
    print x;    #1  2  3
# 返回迭代器对象self._data列表,可避免对象状态被外部修改,或许你会尝试返回tuple,但这需要复制整个
# 列表,浪费更多的内存

'''
iter()很方便,但无法让迭代中途停止,这需自己动手实现迭代器对象。在设计原则上,通常会将迭代器从数据
对象中分离出去,因为迭代器需要维持状态,而且可能有多个迭代器在同时操控数据,这些不该成为数据对象的
负担,无端提升了复杂度
'''
class Data(object):
    def __init__(self,*args):
        self._data = list(args);
    def __iter__(self):
        return  DataIter(self);
class DataIter(object):
    def __init__(self,data):
        self._index = 0;
        self._data = data._data;
    def next(self):
        if self._index >= len(self._data):
            raise StopIteration();
        d = self._data[self._index];
        self._index += 1;
        return d;
d = Data(1,2);
for x in d:
    print x;  # 1 2
#Data 仅仅是数据容器,只需__iter__返回迭代器对象,而由DataIter生成器提供next方法

# 除了for循环,迭代器也可以直接用next()操控
s = Data ('a','b','c');
it = iter(s);
print it  #<__main__.DataIter object at 0x00000000057FF780>
print next(it); #a
print next(it); #b
print next(it); #c
# print next(it); # raise StopIteration();
print '不用next,用for'
for  i in s :
    print i; #a b c




print '---------------------------------------------------------------------'
#--生成器
# 基于索引实现的迭代器有些丑陋,更合理的做法是用yield返回实现了迭代器协议Generator对象
class Data(object):
    def __init__(self,*args):
        self._data = list(args);
    def __iter__(self):
        for x in self._data:
            yield x;
d = Data(1,2,3);
for x in d:
    print x; # 1,2,3


# 编译器会将包含yield的方法或函数重新打包,使其返回Generator对象,这样一来,就无需费力气维护额外的
# 迭代器类型了
print '这是用next'
print d.__iter__()#
print iter(d).next();  #1
print iter(d).next();  #1   ???bug 不懂
print iter(d).next();  #1