Python 迭代器与生成器实例详解
一、如何实现可迭代对象和迭代器对象
1.由可迭代对象得到迭代器对象
例如l就是可迭代对象,iter(l)是迭代器对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
In [ 1 ]: l = [ 1 , 2 , 3 , 4 ]
In [ 2 ]: l.__iter__
Out[ 2 ]: <method - wrapper '__iter__' of list object at 0x000000000426C7C8 >
In [ 3 ]: t = iter (l)
In [ 4 ]: t. next ()
Out[ 4 ]: 1
In [ 5 ]: t. next ()
Out[ 5 ]: 2
In [ 6 ]: t. next ()
Out[ 6 ]: 3
In [ 7 ]: t. next ()
Out[ 7 ]: 4
In [ 8 ]: t. next ()
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StopIteration Traceback (most recent call last)
<ipython - input - 8 - 3660e2a3d509 > in <module>()
- - - - > 1 t. next ()
StopIteration:
for x in l:
print x
for 循环的工作流程,就是先有 iter (l)得到一个t,然后不停的调用t.nex(),到最后捕获到StopIteration,就结束迭代
|
# 下面这种直接调用函数的方法如果数据量大的时候会对网络IO要求比较高,可以采用迭代器的方法
1
2
3
4
5
6
|
def getWeather(city):
r = requests.get(u 'http://wthrcdn.etouch.cn/weather_mini?city=' + city)
data = r.json()[ 'data' ][ 'forecast' ][ 0 ]
return '%s:%s,%s' % (city, data[ 'low' ], data[ 'high' ])
print getWeather(u '北京' )
返回值:<br>北京:低温 13 ℃,高温 28 ℃<br>
|
实现一个迭代器对象WeatherIterator,next 方法每次返回一个城市气温
实现一个可迭代对象WeatherIterable,iter方法返回一个迭代器对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
# -*- coding:utf-8 -*-
import requests
from collections import Iterable, Iterator
class WeatherIterator(Iterator):
def __init__( self , cities):
self .cities = cities
self .index = 0
def getWeather( self ,city):
r = requests.get(u 'http://wthrcdn.etouch.cn/weather_mini?city=' + city)
data = r.json()[ 'data' ][ 'forecast' ][ 0 ]
return '%s:%s,%s' % (city, data[ 'low' ], data[ 'high' ])
def next ( self ):
if self .index = = len ( self .cities):
raise StopIteration
city = self .cities[ self .index]
self .index + = 1
return self .getWeather(city)
class WeatherIterable(Iterable):
def __init__( self , cities):
self .cities = cities
def __iter__( self ):
return WeatherIterator( self .cities)
for x in WeatherIterable([u '北京' ,u '上海' ,u '广州' ,u '深圳' ]):
print x.encode( 'utf-8' )
输出:
北京:低温 13 ℃,高温 28 ℃
上海:低温 14 ℃,高温 22 ℃
广州:低温 17 ℃,高温 23 ℃
深圳:低温 18 ℃,高温 24 ℃
|
二、使用生成器函数实现可迭代对象
1.实现一个可迭代对象的类,它能迭代出给定范围内所有素数
素数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数的数称为素数。
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class PrimeNumbers:
def __init__( self , start, end):
self .start = start
self .end = end
def isPrimeNum( self , k):
if k < 2 :
return False
for i in xrange ( 2 , k):
if k % i = = 0 :
return False
return True
def __iter__( self ):
for k in xrange ( self .start, self .end + 1 ):
if self .isPrimeNum(k):
yield k
for x in PrimeNumbers( 1 , 10 ):
print x
输出:
2
3
5
7
|
三、实现反向迭代
1.反向进行迭代
例如: 实现一个浮点数发生器FloatRange(和xrange类似),根据给定范围(start, end)和步径值(step)产生一系列连续浮点数,如迭代FloatRange(3.0,4.0,0.2)可产生序列:
正向: 3.0 -> 3.2 -> 3.4 -> 3.6 -> 3.8 -> 4.0
反向: 4.0 -> 3.8 -> 3.6 -> 3.4 -> 3.2 -> 3.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
class FloatRange:
def __init__( self , start, end, step = 0.1 ):
self .start = start
self .end = end
self .step = step
def __iter__( self ):
t = self .start
while round (t, 14 ) < = round ( self .end, 14 ):
yield t
t = t + self .step
def __reversed__( self ):
t = self .end
while round (t, 14 ) > = round ( self .start, 14 ):
yield t
t = t - self .step
for x in reversed (FloatRange( 3.0 , 4.0 , 0.2 )):
print x
输出:
4.0
3.8
3.6
3.4
3.2
3.0
for x in FloatRange( 3.0 , 4.0 , 0.2 ):<br> print x<br>输出:<br> 3.0 <br> 3.2 <br> 3.4 <br> 3.6 <br> 3.8 <br> 4.0 <br>
|
上面代码采用round函数是因为浮点数比较会有精度问题,所以需要进行四舍五入
2.对迭代器进行切片操作
例如: 有某个文本文件,想读取其中某范围的内容,如100-300行之间的内容,python中文本文件是可迭代对象,是否可以使用类似列表切片的方式得到一个100-300行文件内容的生成器
使用标准库中的itertools.islice,它能返回一个迭代对象切片的生成器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
f = open ( '/var/log/dmesg' )
from itertools import islice
# 对文件内容100到300行之间进行切片,返回的是个生成器对象,默认歩径是1
islice(f, 100 , 300 )
# 前500行内容
islice(f, 500 )
# 100行到末尾结束内容
islice(f, 100 , None )
ps: 每次使用islice要重新申请对象,它会消耗原来的迭代对象
|
四、 迭代多个对象
1.在一个for语句中迭代多个可迭代对象
1、某班学生考试成绩语文、数学、英语分别存储在3个列表中,同时迭代三个列表,计算三个学生的总分(并行)
2、某年级四个班,某次考试每班英语成绩分别存储在4个列表中,依次迭代每个列表,统计全学年英语成绩高于90分人数(串行)
解决方案:
并行: 使用内置函数zip,它能将多个可迭代对象合并,每次迭代返回一个元组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from random import randint
chinese = [randint( 60 , 100 ) for _ in xrange ( 40 )]
math = [randint( 60 , 100 ) for _ in xrange ( 40 )]
english = [randint( 60 , 100 ) for _ in xrange ( 40 )]
total = []
for c,m,e in zip (chinese, math,english):
total.append(c + m + e)
print total
输出:
[ 204 , 227 , 238 , 201 , 227 , 205 , 251 , 274 , 210 , 242 , 220 , 239 , 237 , 207 , 230 , 267 , 263 , 240 , 247 , 249 , 255 , 268 , 209 , 270 , 259 , 251 , 245 , 262 , 234 , 221 , 236 , 250 , 251 , 249 , 242 , 255 , 232 , 272 , 237 , 253 ]
|
串行: 使用标准库中的itertools.chain,它能将多个可迭代对象连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
from random import randint
from itertools import chain
class1 = [randint( 60 , 100 ) for _ in xrange ( 40 )]
class2 = [randint( 60 , 100 ) for _ in xrange ( 42 )]
class3 = [randint( 60 , 100 ) for _ in xrange ( 39 )]
class4 = [randint( 60 , 100 ) for _ in xrange ( 43 )]
count = 0
for s in chain(class1, class2, class3, class4):
if s > 90 :
count = count + 1
print count
输出:
38
|
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
原文链接:https://my.oschina.net/zhangyangyang/blog/890286