# -*- 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