# python 高级应用 第三章

3-1 ,2如何实现可迭代对象和迭代器对象

实际案列:某软件要求,从网络抓取各个城市的信息,并以此显示:
北京:15-20

天津:17-22

长春:12-18

如果一次抓取所有城市天气再显示,显示第一个城市气温时,有很高的延迟,并且浪费存储空间,我们期望其“用时访问”的策略,并且能够把所有城市气温封装到一个对象里,可以使用for来进行迭代。

首先介绍可迭代对象和迭代器:

可迭代对象可以得到跌倒器,for循环的工作原理,for循环首先调用可迭代对象的__ iter__ 方法,然后获得一个迭代器在调用迭代器的next方法,不停地输出对象。

l=[1,2,3,4,5]
s='abcde'
for x in l : print x ## in 后面的对象必须保证是可得带对象,

t=iter(l)
t.next()#每调用一次打印出一个元素

解决方案:

step1:实现一个迭代器对象WeatherIterator , next 方法每次返回一个城市气温。

step2:实现一个可迭代对象WeatherIterator, next 方法返回一个可迭代对象。

import requests
def getWeather(city):
    r=requests.get(u'http://wthrcdn.etouch.cn/weather_mini?city='+city)
    data=r.json()['data']['forecast'][0]
    return '%$: %$, %$' %(city,data['low'],data['high'])

print(getWeather('北京'))
print(getWeather('长春'))

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 '%$: %$, %$' %(city,data['low'],data['high'])
    def next(self):
        if self.index==len(self.cities):
            raise.StopIteration
        city=self.cities[self.index]
        self.index +=
        return self.getWeather(city)
    
 class WeatherIterable(Iterable):
    def __init__(self.cities):
        self.cities=cities
    def __init__(self):
        return WeatherIterator(self.cities)
 for x in WeatherIterable([u"北京,上海,广州,长春"]):
    print(x)
    
    

3-3 如何使用生成器来实现可迭代对象

实际案例:实现一个可迭代对象的类,它能够迭代出给定范围内的所有的素数:

pn=PrimeNumber(1,30)
for k in pn:
    print K
# 输出结果:2,3,5,7,11,13,17,19

解决方案:将该类的__ iter__ 方法实现成生成器函数,每次yield返回一个素数

# 首先我们来看下生成器的工作原理
#!/python2
def f():
    print 'in f(), 1'
    yield 1
    
    print 'in f(), 2'
    yield 2
    
    print 'in f(), 3'
    yield 3
    
 g=f() #没有东西打印出来
print(g.next()) #打印出‘in f(), 1

#生成器对象也是一个可迭代对象,可迭代对象也就说它可以放在in后面,
for x in g:
    print(x)
    
# 结果就进行了迭代,也就是说g实现了g.__iter__ 这个方法 就是它自身
# 验证:
print (g.__iter__() is g)

综上所述,生成器对象和迭代器对象的行为完全一致,把可迭代对象的__ iter()__方法变为生成器函数

#!/usr/bin/python2
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,100):
    print(x)
        
        
    

如何进行反向迭代以及如何实现反向迭代

实际案例:实现一个连续浮点数发生器FloatRange (和xrange )类似,根据给定的范围(start, end)和进步值(step)来生成一系列连续的浮点数,如迭代FlostRange (3.0, 4.0, 0.2)可产生序列:

正向:3.0->3.2->3.4->.....->4.0

反向:4.0->3.8->3.6->......->3.0

正向就是实现一个可迭代对象,就是实现它的__ iter__ 方法,reversed同样也需要支持__ reverse__ 方法

l=[1,2,3,4,5]
l.reverse() #改变了原列表,l为[5,4,3,2,1]
# 在不允许改变原列表的情况下这个方法就用不了
1[::-1] #使用切片的方法,虽然并没有改变原列表但是生成了一个和原列表同样大小的一个列表,这样就很占内存

# 推荐使用函数reversed(),其原理和迭代器是一样的,只不过迭代器iter()返回的正向的值,而reversed()返回的是反向的迭代对象
for x in reversed(l):
    print(x)

具体实现

#!/usr/bin/python2
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 t <= self.end:
            yield t
            t += self.step
            
    def __reversed__(self): #反向迭代
        t = self.end
        while t >= self.start:
            yield t
            t -= self.step
for x in FloatRange(1.0, 4.0, 0.2): #此时调用的是FloatRange的内部函数__iter__
    print(x)
# 如果是反向迭代的话:
for x in reversed(FloatRange(1.0, 4.0, 0.2)): #此时调用的是FloatRange内部的__reversed__ 函数
    print(x)

3.4 如何对得带器进行切片操作

实际案例:有某个文本文件, 我们想读取我中某范围内的内容,如100-300行之间的内容,python中的文本文件是可迭代对象,我们是否可以使用类似列表切片的方式得到一个100~300行文件内容的生成器?

# 简单的实现方法
f=open('English.txt')
lines=f.readlines()
lines[2:4] #这就可以使用列表切片,但是存在的问题是,readlines会把整个文档读取,当文件很小的时候,还好,如果文件很大,不仅会占用很多的内存,同时也很费时间。

#下面使用迭代的方法
for line in f:
    print(line) #结果没有输出是因为刚才的readlines将文件的指针定位到文件的最后。
f.seek(0) #将文件指针定位到文件的开头
for line in f:
    print(line) #现在就可以逐行返回    

解决方案:使用标准库中的itertools.slice ,它能够返回一个迭代对象切片的生成器。

from itertools import islice
help(islice)
#islice(iterable, [start],stop,[step]) #islice的三个参数
f=open('English.txt')
for i in islice(f, 7, 16):
    print(i)
islice(f,10) #传入得到是前10行的内容
islice(f,10,None) #传入第10行到最后一行
## islice不支持负的参数,因为我们在没有读完整个文件之前,是不知道文件有多少行。

islice 函数的解析:

l=range(20) #生成1到20范围内的列表
t=iter(l) #将l转换成一个迭代器
for x in islice(t,5,10):
    print(x)
#原理解释: t是整个都被迭代过的,但是前面的1到5的数字不满足要求,所有被过滤掉了,直到遇到在5到10行的值才被返回
# 如果接下来继续迭代对象的话就是从10开始,因为之前的代码已经迭代到10了
for x in t:
    print(x)

3-6 如何在一个for语句中迭代多个可迭代对象

实际案例:

1.某班学生期末考试成绩,语文,数学,英语分别存储在3个列表中,同时迭代三个列表,计算每个学生的总分。(并行)

# 简单的实现方法:
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)]
for i in xrange(len(math)):
    sumUp=chinese[i] + math[i] + English[i]
    print(sumUp)

解决方案:

并行->并行:使用内置函数zip, 它可以将多个迭代对象合并,每次迭代返回一个元组。(zip函数在连接的时候,如果长度不一致的话,就会取较短的一组元素的长度)

# 使用zip函数来实现
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 a,b,c in zip(chinese, math, English):
    total.append(a + b + c)
print(total)

2.某年级有4个班,某次考试每班的英语成绩分别存储在4个列表中,依次迭代每个列表,统计全学年成绩高于90分的人数。(串行)

串行->使用标准库中的itertools.chain, 它能够将多个迭代对象连接

from random import randint
from itertools import chain
class1=[randint(60,100) for _ in xrange(46)]
class2=[randint(60,100) for _ in xrange(47)]
class3=[randint(60,100) for _ in xrange(43)]
class4=[randint(60,100) for _ in xrange(50)]
count=0
for s in chain(class1, class2, class3, class4):
    if s > 90:
        count +=1        
print(count)

你可能感兴趣的:(# python 高级应用 第三章)