python 实用编程技巧 —— 对象迭代与反迭代相关问题与解决技巧
如何实现可迭代对象和迭代器对象
从网络抓取各个城市气温,并依次显示: 北京:12-16 上海:20-30 ... 如果一次抓取所有城市天气再显示,显示第一个城市气温时,有很高的延时,并且浪费存储空间,我们期望以'用时访问的策略', 并且能把所有城市气温封装到一个对象里,可用for语句进行迭代,如何解决?
import requests from collections import Iterable, Iterator class WeatherIterator(Iterator): '''实现一个迭代器对象WeatherIterator''' def __init__(self, cities): self.cities = cities # 记录迭代位置 self.index = 0 def getWeather(self, city): '''获取单个城市气温数据''' r = requests.get('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): '''__next__方法每次返回一个城市气温''' if self.index == len(self.cities): raise StopIteration city = self.cities[self.index] self.index += 1 return self.getWeather(city) class WeatherIterable(Iterable): '''实现一个可迭代对象WeatherIterable''' def __init__(self, cities): self.cities = cities def __iter__(self): '''__iter__方法返回一个迭代器对象''' return WeatherIterator(self.cities) if __name__ == '__main__': for city in WeatherIterable(['北京', '上海', '广州', '深圳']): print(city)
如何使用生成器函数实现可迭代对象
实际案例:
- 实现一个可迭代对象的类,它能迭代出给定范围内的所有素数: pn = PrimeNumber(1, 30) for k in pn: print k
- 输出结构: 2 3 5 7 11 13 17 19 23 29
解决方案: 将该类的 iter 方法实现成生成器函数,每次yield返回一个素数
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 def __iter__(self): for k in range(self.start, self.end + 1): if self.isPrimeNum(k): yield k if __name__ == '__main__': for x in PrimeNumbers(1, 10): print(x)
如何进行正向迭代和反向迭代
实际案例:
- 实现一个连续浮点数发生器 FloatRange ( xrange 类似), 根据给定范围(start, end)和步进值(step)产生一系列连续浮点数, 如迭代FloatRange(3.0, 4.0, 0.2)可产生序列
实现正向和反向连续浮点序列
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 if __name__ == '__main__': print("正向序列") for x in FloatRange(1.0, 4.0, 0.5): print(x) print("反向序列") for x in reversed(FloatRange(1.0, 4.0, 0.5)): print(x)
如何对迭代器做切片操作
islice能返回一个迭代对象切片的生成器
from itertools import islice f = open('zhang.txt') print(islice(f,100,300))
读取 3-5 行
from itertools import islice f = open('zhang.txt') for line in islice(f,3,5): print(line)
islice(f,30)# 读取前30行 islice(f,50,None)# 读取50到最后一行
如何在一个for语句中迭代多个可迭代对象
实际案例:
- 某班学生期末成绩,语文,数学,英语分别存储在3个列表中, 同时迭代三个列表,计算每个学生的总分.(并行)
解决方案:
- 并行: 使用内置函数zip,它能将多个可迭代对象合并,每次迭代返回一个元组
使用 zip 函数
from random import randint chinese = [randint(60,100) for _ in range(10)] math = [randint(60,100) for _ in range(10)] english = [randint(60,100) for _ in range(10)] total = [] for c, m, e in zip(chinese, math, english): total.append(c + m + e) print(total)
实际案例:
- 某年及有4个班,某次考试每班英语成绩分别存储在4个列表中, 依次迭代每个列表,统计全学年成绩高于90分人数.(串行)
解决方案:
- 串行: 使用标准库中的itertools.chain,它能将多个可迭代对象连接
from random import randint from itertools import chain c1 = [randint(60,100) for _ in range(40)] c2 = [randint(60,100) for _ in range(41)] c3 = [randint(60,100) for _ in range(42)] c4 = [randint(60,100) for _ in range(43)] count = 0 for s in chain(c1,c2,c3,c4): if s > 90: count += 1 print(count)