一、迭代器
1、初探
之前章节中,我们看到for语句可以Python任何序列类型,包括列表、元祖以及字符串。如下所示:
>>>foriin[1,2,3]:print(i,end=' ')...
123
>>>foriin(1,2,3):print(i,end=' ')...
123
>>>foriin'123':print(i,end=' ')...
123
下面我们继续看文件迭代器、手动迭代(iter, next)、其它内置类型迭代器。
2、文件迭代器
回忆一下之前章节,文件访问方式有如下:
.read(): 一次性读取全部内容。
.readline(): 一次读取一行。
.readlines(): 生成列表,每一行是每个元素。
.next(): 跟readline()差不多,但读取完之后报错。
next()报错,为StopIteration。在Python中任何这类对象都认为是可迭代的。在Python里迭代工具(比如for)会调用next()来获取数据,并以StopIteration来确认何时离开。
forlineinopen(): print(line, end ='')
因为每个元素在最后包涵\n(换行符),因此print函数结尾需要取消结尾中的换行。
注: 尽量不要使用readlines()函数,因为这个会一次性的把所有内容读取到内存里(转换为列表),运行速度会比较慢。最好使用readline或者迭代文件方法。
3、手动迭代
为了支持手动迭代代码,Python支持next()函数,它会自动读取next()函数。next(X)等同于X.next()。
f = open() next(f)
这个会从第一行开始读取内容。
从技术角度来讲,迭代协议里,当使用for函数进行迭代时,会传递给iter内置函数,以便可迭代对象中获取迭代器。返回的对象有需要有next()方法。
>>>L = [1,2,3]
>>>i = iter(L)
>>>next(i)
1
>>>next(i)
2
>>>next(i)
3
>>>next(i)Traceback (most recent call last): File"", line1,inStopIteration
对于文件来说,不需要转换成iter类型的这一步:
>>>file = open(r'C:\Test\test.txt')
>>>fileisiter(file)
True
因为文件对象就是自己的迭代器,但列表,元祖,字符串等就不是了。
>>>str ='123'; list = [1,2,3] ; tuple = (1,2,3)
>>>strisiter(str)
False
>>>listisiter(list)
False
>>>tupleisiter(tuple)
False
4、其他内置类型迭代器
除了文件以及像列表这样的实际的序列外,其它类型也有其适用的迭代器。例如,遍历字典键的经典方法是明确地获取其键的列表。
>>>dict = {'a':1,'b':2,'c':3}
>>>forkeyindict.keys() :
...print(key,dict[key])...a1
b2
c3
二、列表解析
1、初探
遍历列表时,使用for循环来修饰它:
>>>list = [1,2,3]
>>>foriinrange(len(list)) :
...list[i] +=10
...
>>>list[11,12,13]
这个是有效的,但不是“最佳实践”。我们可以使用产生所需结果列表的一个单个表达式来完成上面循环:
>>>list = [1,2,3]
>>>list = [i +10foriinlist]
>>>list[11,12,13]
2、列表解析基础知识
从之前的例子开始分析:list1 = [i + 10 for i in list1]
这个先是运算 [i + 10 for i in list1]之后,再把此赋值给list1。我们来看里面是如何运算:
先是对list1进行迭代,每次把单个值符给i,再进行i + 10,成为新列表的单个元素。这个相当于:
tmp = []
foriinlist1:tmp.append(i+10)list1 = tmp
可以认为是倒过来的for循环语句。
3、在文件上使用列表解析
>>>file = open(r'C:\Test\test.txt').readlines()
>>>file['DoraEmon\n','Daxiong\n','JingXiang']
在这里发现,每行最后都会有\n换行符,这个时候我们可以使用列表解析来进行去除换行符的操作。
>>>file = [line.strip()forlineinfile]
>>>file['DoraEmon','Daxiong','JingXiang']
或更简单
>>>file = [line.strip()forlineinopen(r'C:\Test\test.txt')]
还可以进行更多操作:
>>>file = [line.strip().upper()forlineinopen(r'C:\Test\test.txt')]
>>>file['DORAEMON','DAXIONG','JINGXIANG']
4、扩展的列表解析语法
扩展语法是在循环基础上,添加了条件:
>>>file = [line.strip().upper()forlineinopen(r'C:\Test\test.txt')ifline[] !='J']
>>>file['DORAEMON','DAXIONG']
在这里,发现添加 if line[0] != J’的时候,不显示以 “ J “ 开头的行。
>>>[x+yforxin[1,2,3]foryin[10,20,30]][11,21,31,12,22,32,13,23,33]
我们会发现第二个for循环算是嵌套在第一个for循环。
三、其他迭代环境
map也可用在迭代中:
>>>list(map(str.upper, open(r'C:\Test\test.txt')))['DORAEMON\n','DAXIONG\n','JINGXIANG']
map函数是把后面的可迭代的每个值当作前面的参数传入。在Python3开始正式引入map,之前版本,python2.7也可以使用,但map可以直接返回列表,不需要使用list函数进行转换。后续章节中会继续讲解。上面的可以如下解释:
>>>tmp = []
>>>forlineinopen(r'C:\Test\test.txt'):
...tmp.append(str.upper(line))...
>>>tmp['DORAEMON\n','DAXIONG\n','JINGXIANG']
相应的,也有sorted,会对迭代对象进行排序后生成列表。
>>>sorted(open(r'C:\Test\test.txt'))['Daxiong\n','DoraEmon\n','JingXiang']
enumerate也会对迭代对象进行运算后,生成可迭代对象。
>>>list(enumerate(open(r'C:\Test\test.txt')))[(,'DoraEmon\n'), (1,'Daxiong\n'), (2,'JingXiang')]
enumerate就是在原有的顺序中添加序列号。除了这些,还有filter与reduce方法,这些再后续章节讲解用法。
>>>list(filter(bool,open(r'C:\Test\test.txt')))['DoraEmon\n','Daxiong\n','JingXiang']
>>>importoperator,functools
>>>functools.reduce(operator.add,open(r'C:\Test\test.txt'))
'DoraEmon\nDaxiong\nJingXiang'
sum、any、all、max、min也可使用迭代器。
any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、FALSE 外都算 TRUE。
all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。元素除了是 0、空、None、False 外都算 True。
>>>sum((1,2,3)),sum([4,5,6])(6,15)
>>>any([1,[],'True']), all([1,[],'True'])(True,False)
>>>max([2,3,1]),min([2,3,1])(3,1)
list, tuple, join 都是可以对可迭代对象进行操作后输出:
>>>list(open(r'C:\Test\test.txt')),tuple(open(r'C:\Test\test.txt')),'**'.join(open(r'C:\Test\test.txt'))(['DoraEmon\n','Daxiong\n','JingXiang'], ('DoraEmon\n','Daxiong\n','JingXiang'),'DoraEmon\n**Daxiong\n**JingXiang')
四、Python3中新的迭代环境
从Python3.x开始,更注重迭代。在Python2.x里,很多函数生成的是列表方式:
>>>zip('abc','123')[('a','1'), ('b','2'), ('c','3')]
但在Python3.x开始是变成可迭代的特定对象:
Python 3.x的这种方式,会延迟计算,在提取内容的时候计算结果。这样会节省内存空间,不需要提前计算后放进内存里。迭代对象,当迭代完成之后,不能再次读取。
>>>a = list(zip('abc','123'))
>>>foriina: print(i, end=' ')...('a','1') ('b','2') ('c','3') >>>
>>>foriina: print(i, end=' ')...>>>
1、range
range从Python3.x开始变为迭代器。
Python 2.x:
>>>range(5)[,1,2,3,4]
Python 3.x:
>>>range(5)range(,5)
所以,在Python 3.x要是需要生成一个有序列表,需要再次转换:
>>>list(range(5))[,1,2,3,4]
但也可以像列表一样使用:
>>>range(5)[1]
1
>>>range(5)[-1]
4
注:range可继续使用。
>>>r = range(7)
>>>foriinr :
...print(i,end =' ')...
123456>>>
>>>foriinr :
...print(i,end =' ')...
123456>>>
2、map、zip和filter
在Python3.x里,map、zip和filter也是使用迭代器来节约内存开销。但与range不同,使用一次之后,就不能再次使用。
>>>M = map(abs,(-1,3,7))
>>>list(M)[1,3,7]
>>>list(M)[]
>>>Z = zip((1,2,3),('a','b','c'))
>>>list(Z)[(1,'a'), (2,'b'), (3,'c')]
>>>list(Z)[]
>>>F = filter(bool,(1,'','True'))
>>>list(F)[1,'True']
>>>list(F)[]
map() 函数会根据提供的函数对指定序列做映射。第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。我们可以使用 list() 转换来输出列表。如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
五、多个迭代器 vs 单个迭代器
之前看到的range,可以同时使用多个迭代器。这个叫range有着多个迭代器。可以进行索引。但其它的,只能迭代一次。
后续章节会看到各种迭代器,而且会说明如何生成两种迭代器。
六、字典视图迭代器
字典视图迭代器,与其它多个迭代器相似(在Python2.x里还是使用列表)
>>>dict = {'a':1,'b':2,'C':3}
>>>dict.keys()dict_keys(['a','b','C'])
>>>dict.values()dict_values([1,2,3])
>>>dict.items()dict_items([('a',1), ('b',2), ('C',3)])
在这里,迭代器都算是多个迭代器(Python3.4为基准)
领取专属 10元无门槛券
私享最新 技术干货