某软件要求,从网络抓取各个城市气温信息,并一次显示:
北京 15–20
天津 17–22
…….
如果一次抓取所有城市天气在显示,显示第一个城市气温时,有时出现延时,并浪费存储空间,我们期望以’用时访问‘(惰性访问)策略,并且能把所有城市气温封装到一个对象里,可用for语句进行迭代。如果解决?
解决方法
#方法1
import requests
import json
def getWeather(city):
#获取一个网页的响应数据
re=requests.get('http://wthrcdn.etouch.cn/weather_mini?city='+city)
#通过json解码得到字典
d=json.loads(re.text)
#这一步可以通过打印来查询子元素对象
data=d['data']['forecast'][0]
return '%s:%s,%s' % (city,data['low'],data['high'])
print( getWeather('上海'))
print(getWeather('广州'))
#方法2--通过迭代器与迭代对象完成
from collections import Iterable, Iterator
#定义一个天气得迭代器
class WeatherIterator(Iterator):
#定义构造器 cites为城市列表
def __init__(self,cities):
self.cities=cities
#定义一个初始化得列表位置
self.index=0
#定义迭代器功能函数
def getWeather(self,city):
re = requests.get('http://wthrcdn.etouch.cn/weather_mini?city=' + city)
# 通过json解码得到字典
d = json.loads(re.text)
data = d['data']['forecast'][0]
return '%s:%s,%s' % (city, data['low'], data['high'])
#迭代器实现得方法--获取到迭代器中的元素,相当于next(Iterator)
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(['芜湖','北京','上海','广州','深圳']):
print(x)
注意:
1.python3中常见的可迭代对象(iterable)有字符串,列表,字典,元组,集合等;
2.迭代器(iterator)是指能够用iter(obj)函数返回的对象(实例),并且通过next(it)函数获取可迭代对象的值;
3.相关的函数有:iter(iterable)—生成一个迭代器
next(iterator)—-获取下一个记录,或没有记录时,或出发StopIterator异常
4.for循环的工作原理:通过可迭代对象的iter方法得到一个迭代器,然后不停地调用next方法,知道出发StopIterator异常,则停止。
实现一个可迭代对象的类,它能迭代出给定范围内所有素数 pn=PrimeNumbers(1,30)
for k in pn: print(k)
输出结果:2 3 5 7 11 13 17 19 23 29
解决办法
#生成器函数工作原理
def f():
print('in f(),1')
yield 1
print('in f() ,2')
yield 2
print('in f(),3')
yield 3
g=f()
# print(g is g.__iter__()) #说明给方法与迭代器类似
for x in g:
# print(x)
pass
#用生成器的方法实现
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 range(2,k):
if k%i==0:
return False
return True
#定义生成器---该类的‘__iter__’方法实现生成器函数,每次yield返回一个素数
def __iter__(self):
for k in range(self.start,self.end):
if self.isPrimeNum(k):
yield k
for x in PrimeNumbers(1,100):
print(x,end=' ')
print()
注意:
1.生成器是一个能够动态提供数据的对象(通过def–yield定义),生成器对象是可迭代对象,因此可以直接通过for语句取值;
2.生成器,即是可迭代对象,也是迭代器;因此,可以通过next(it)函数来取yiekd生成的数据
实现一个连续浮点数发生器FloatRange(类似range),根绝给定范围(start,end)和步长(step)产生一些列连续浮点数,如迭代FloatRange(3.0,4.0,0.2)可产生序列:
正向:3.0–3.2–3.4—3.6
反向: 4.0–3.8–3.6–3.4–3.2
解决办法
#利用迭代器解决正向与反向问题
class FloatRange:
def __init__(self,start,end,step):
self.start=start
self.end = end
self.step = step
#正向迭代
def __iter__(self):
t=self.start
while t<=self.end:
yield t
t +=self.step
#反向迭代接口---实现反向迭代协议的__reversed__方法,它返回一个反向迭代器
def __reversed__(self):
t=self.end
while t>=self.start:
yield t
t -=self.step
for x in FloatRange(1.0,4.0,0.5):
print(x,end=' ')
print()
for x in reversed(FloatRange(1.0,4.0,0.5)):
print(x,end=' ')
有某个文本文件,我们想读取其中某范围的内容,如100-300行之间的内容,python中文本文件时可迭代对象,我们是否可以使用类似列表切片的方式得到一个100-300行文件内容的生成器?
解决方法
#切片操作
f=open('filename')
#不可以直接对文本进行切片 f[100:300]
lines=f.readlines() #将文件一次性读入一个列表中,若文件很大,那可能导致内存不足
for line in f:
print(line)
from itertools import islice
for line in islice(f,100,300):
print(line)
#可迭代对象是被消耗的
l=list(range(10))
t=iter(l)
for i in islice(t,3,6):
print(i,end=' ')
print('**********************')
for i in t:
print(i,end=' ')
1.某班学生期末成绩,语文,数学,英语分别存储在3个列表中,同时迭代三个列表,计算每个学生的总分(并行)
2.某年纪有4个班,某次考试每班英语成绩分别储存在4个列表中,依次迭代诶个列表,统计全学年成绩高于90跟的人数(串行)
解决办法
#方法1--for遍历
from random import randint
chinese=[randint(40,100) for _ in range(20)]
math=[randint(40,100) for _ in range(20)]
english=[randint(40,100) for _ in range(20)]
#存在的问题,有些生成器不能通过索引的方式访问
for i in range(len(chinese)):
my_sum=chinese[i]+math[i]+english[i]
print(my_sum,end=' ')
print()
#方法2---zip函数,合并多个可迭代对象
total=[]
for c,m,e in zip(chinese,math,english):
total.append(c+m+e)
#方法3--itertools.chain函数 连接多个可迭代对象
e1=[randint(40,100) for _ in range(20)]
e2=[randint(40,100) for _ in range(28)]
e3=[randint(40,100) for _ in range(19)]
e4=[randint(40,100) for _ in range(31)]
from itertools import chain
count=0
for i in chain(e1,e2,e3,e4):
if i >90:
count +=1