码迷,mamicode.com
首页 > 其他好文 > 详细

迭代,迭代器,生成器

时间:2018-01-02 18:44:18      阅读:118      评论:0      收藏:0      [点我收藏+]

标签:迭代迭代器生成器

# -*- coding:utf-8 -*- # 迭代--------- # 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历称为迭代 # 在python中,迭代通过for...in完成,而很多语言比如c或者Java,迭代通过下标完成,如Java代码 ''' for (i = 0,i<list.length;i++){ n = list[i] } 可以看出,python的for循环抽象成都要高于Java的for循环,因为python的for循环不仅仅可以用在list 或者tuple上,还可作用在其他迭代器对象上,list这种数据类型虽然有下标,但很多其他数据类型是没 有下标的,但是只要是可迭代的对象,无论有无下标,都可以迭代,比如dict就可以 ''' d = dict(a = 1,b = 2,c = 3); print d;  #{'a': 1, 'c': 3, 'b': 2} for key in d:     print key;  # a c b dict不是按照list顺序排列,所以,迭代出的结果顺序可能不一样。 #默认情况下 dict迭代的是key,如果要迭代value,可以用forvalue in d.itervalues(),如果要同时 # 迭代key和value,可以用for k,v in d.iteritems() # 字符串也是可迭代对象,因此,也可以作用于for循环 for c in 'abcd':     print c; #a b c d #所以当使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象是list # 还是其他数据类型 #判断一个对象是否可迭代用collections模块的Iterable from collections import Iterable print isinstance('xyz',Iterable); #True str print isinstance([1,2,3],Iterable); #True list print isinstance(123,Iterable); # False 整数 print isinstance((1,2,3),Iterable); #True 元组 # 如果要对list实现类似Java那样下标循环,python内置的enumerate函数可以吧一个list变成索引元素对 # 这样就可以for循环中同时迭代索引和元素本身 for i,value in enumerate (['a','b']):     print i,value; ''' 0 a 1 b     ''' # 上面for循环里,同时引用了两个变量,在python中很常见,如下面代码 for x,y in [(1,1),(2,3),(4,5)]:     print x,y; ''' 1 1 2 3 4 5 ''' #------------------------------------------------------------------------- #--------包装----------------- # 用functools.partial()可以将函数包装成更简洁的版本 from functools import partial def test(a,b,c):     print a,b,c; f = partial(test,b = 2,c = 3); #为后续参数提供默认命名值 print f(7); # 7 2 3 f = partial(test,7,c = 3) #为前面位置参数和后面的命名参数提供默认值 print f(5); # 7 5 3 #python会按下面的规则合并参数 from functools import partial def partial(func,*d_args,**d_kwargs):     def wrap(*args,**kwargs):         new_args = d_args + args;  #合并位置参数,partial提供的默认值优先         nwe_kwargs = d_kwargs.copy(); #合并命名参数,partial提供的会被覆盖         new_kwargs.update(kwargs);         print new_args;         print nwe_kwargs;         return fuc(*new_args,**new_kwargs);     return wrap     f = partial(1,'ddd',b = 2)     wrap('aa',c = 1) #暂未输出 #------------------------------------------------------------------- #---列表生成式 ''' 列表生成式即list comparehensions,是python内置的非常简单却很强大的可以用来创建list的生成式 举例生成list[1,2,3,4,5,6,7]可以用list(xrange(1,8)) ''' print list(range(1,8)); #[1, 2, 3, 4, 5, 6, 7] #生成[1*1,2*2,...7*7] #循环 l = []; for x in xrange(1,8):     l.append(x*x); print l; #[1, 4, 9, 16, 25, 36, 49] #列表生成式 print list([x * x for x in xrange(1,8)]);#[1, 4, 9, 16, 25, 36, 49] print [x * x for x in xrange(1,8)] #[1, 4, 9, 16, 25, 36, 49] # 写列表生成式时,要把生成的元素x*x放在前面,后面跟for循环,就可以把list创建出来 # for后可以加IF判断,这样就可以筛选出偶数或奇数的平方 print [x * x for x in xrange(1,8) if x % 2 ==0];#[4, 16, 36] print [x * x for x in xrange(1,8) if x % 2 != 0]; #[1, 9, 25, 49] # 用两层循环生成全排列 print [m + n for m in 'ABC' for n in '1','2','3'] # ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'] # 列出当前目录下的所有文件和目录名 import os #导入os模块 print [s for s in os.listdir('.')]; ''' '['aaa.py', 'demo1.12.25.py', 'demo2.12.26.py', 'demo3.12.26.py', 'demo4.12.27.py',   'demo5.12.27.py', 'demo6.12.28.py', 'demo7.01.2.py', 'Include', 'Lib', 'Scripts', 'tcl'] ''' # for循环可以同时使用两个甚至多个变量,比如dict的items()可以同事迭代key和value d = dict(a = 'A',b = 'B',c = 'c'); print d; #{'a': 'A', 'c': 'c', 'b': 'B'} for k,v in d.items():     print k,':',v ''' a : A c : c b : B ''' #列表生成式也可以使用两个变量来生成list; d = dict(x = 'X',y = 'Y',z = 'Z'); print [k + '=' + v for k, v in d.items()] #['y=Y', 'x=X', 'z=Z'] #将一个list中的所有字符串变小写 L = ['HHHe','WWo','APPLE']; print [s.lower() for s in L]; #['hhhe', 'wwo', 'apple'] #--------------------------------------------------------------------------- #---生成器-- ''' 通过列表生成式,我们可以直接创建一个表,但是受到内存限制,列表容量肯定是有限的。而且,创建一个包含 100万个元素的列表,不仅占用很大的存储空间,如果我们只访问前面的几个元素,那后面绝大多数元素占用的 空间都浪费了 所以,如果列表元素可以按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就 不必创建完整的list,从而节省大量的空间,在python中,这种一边循环,一边计算的机制交生成器 generator 创建一个generater,有很多方法,第一种简单的方法,只要把一个列表生成式的[]改成() ''' L = [x * x for x in xrange(1,5)]; #[1, 4, 9, 16] print L; g = (x * x for x in xrange(1,5)); print g ;  #<generator object <genexpr> 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<x:         print (b);         a,b = b,a+b;         n = n+1;     return 'done'; fib(5); # 1,1,2,3,5 ''' a,b = b,a + b 相当于 t = (b,a + b); #t是一个tuple a = t[0]; b = t[1]; 但不必显式写出临时变量t就可以赋值 上面的函数可以输出斐波纳挈数列的前n个数 仔细观察,fib函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算后续任意的元素,这种 逻辑很类似generator 也就是说,上面的函数和generator仅一部之遥,要把fib函数变成generator,只把print(b)改为yield(b) ''' def fib(x):     n, a, b = 0, 0, 1     while n < x:         yield b         a, b = b, a + b         n = n + 1 print fib(6) #<generator object fib at 0x000000000503A5A0> # 这是定义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__()#<generator object __iter__ at 0x0000000004BACC60> print iter(d).next();  #1 print iter(d).next();  #1   ???bug 不懂 print iter(d).next();  #1

迭代,迭代器,生成器

标签:迭代迭代器生成器

原文地址:http://blog.51cto.com/11927232/2056734

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!