例如,某天气软件要求,通过网络抓取各个城市气温信息,并依次显示:
北京:15 ~ 20
天津:17 ~ 22
长春:12 ~ 18
...
如果一次抓取所有城市气温信息再显示,显示第一个城市气温时,有很高的延时,并且浪费存储空间。
要求:以“用时访问”的策略,并将所有城市气温信息封装到一个对象里,可用for循环进行迭代。
解决方案:
Step1:实现一个迭代器对象WeatherIterator,它的__next__()
方法每次返回一个城市的气温;
Step2:实现一个迭代器对象WeatherIterable,它的__iter__()
方法返回一个WeatherIterator对象。
- 对于
Iterable
类:
>>> from collections.abc import Iterable
>>> issubclass(int, Iterable)
False
>>> issubclass(str, Iterable)
True
>>> issubclass(list, Iterable)
True
>>> issubclass(dict, Iterable)
True
Iterable
类是提供了__iter__()
方法的抽象基类。iterable表示可迭代对象,即能够逐一返回其成员项的对象。可迭代对象被可用于for循环以及许多其他需要一个序列的地方(zip()
、map()
、…)。当一个可迭代对象作为参数传给内置函数iter()
时,它会返回该对象的迭代器。
使用isinstance(obj, Iterable)
可以检测一个对象是否已经注册到Iterable
或者实现__iter__()
函数;但检测一个对象是否是可迭代对象的唯一可信赖的方法是调用 iter(obj)
。
- 对于
Iterator
类:
>>> from collections.abc import Iterator
>>> issubclass(int, Iterator)
False
>>> issubclass(str, Iterator)
False
>>> issubclass(list, Iterator)
False
>>> issubclass(dict, Iterator)
False
Iterator
类是提供了__iter__()
和__next__()
方法的抽象基类,它继承Iterable
类。iterator表示迭代器对象,即用来表示一连串数据流的对象。重复调用迭代器的__next__()
方法(或将其传给内置函数next()
)将逐个返回流中的项。
迭代器必须具有__iter__()
方法用来返回该迭代器对象自身,因此迭代器对象必定也是可迭代对象,但可迭代对象不一定是迭代器对象。
Iterable
与Iterator
区别:
判断是否是Iterable
与Iterator
:
isinstance(obj, Iterable)
isinstance(obj, Iterator)
凡是可以for循环的,都是Iterable;凡是可以next()
的,都是Iterator。Iterable是一次性消费的,不同的Iterable之间没有关联。Python中的for循环就是通过next()
实现的。
集合数据类型如list、truple、dict、str,都是Itrable不是Iterator,但可以通过iter()
函数获得一个Iterator对象。
- 方案示例:
使用迭代器:
from collections.abc import Iterable, Iterator
import requests
class WeatherIterator(Iterator):
def __init__(self, cities):
self.cities = cities
self.index = 0
def __next__(self):
if self.index == len(self.cities):
raise StopIteration
city = self.cities[self.index]
self.index += 1
return self.get_weather(city)
def get_weather(self, city):
url = 'http://wthrcdn.etouch.cn/weather_mini?city=' + city
r = requests.get(url)
data = r.json()['data']['forecast'][0]
return city, data['high'], data['low']
class WeatherIterable(Iterable):
def __init__(self, cities):
self.cities = cities
def __iter__(self):
return WeatherIterator(self.cities)
def show(w):
for x in w:
print(x)
w = WeatherIterable(['北京', '上海', '深圳'] * 3)
show(w)
结果:
('北京', '高温 33℃', '低温 20℃')
('上海', '高温 35℃', '低温 27℃')
('深圳', '高温 32℃', '低温 26℃')
('北京', '高温 33℃', '低温 20℃')
('上海', '高温 35℃', '低温 27℃')
('深圳', '高温 32℃', '低温 26℃')
('北京', '高温 33℃', '低温 20℃')
('上海', '高温 35℃', '低温 27℃')
('深圳', '高温 32℃', '低温 26℃')
通过迭代器对象,实现“用时访问”,每次返回一个城市的气温信息。可迭代对象类WeatherIterable必须要定义,因为迭代器对象是一次性的。
- 方案示例:
使用生成器:
from collections.abc import Iterable
import requests
class WeatherIterable(Iterable):
def __init__(self, cities):
self.cities = cities
def __iter__(self):
for city in self.cities:
yield self.get_weather(city)
def get_weather(self, city):
url = 'http://wthrcdn.etouch.cn/weather_mini?city=' + city
r = requests.get(url)
data = r.json()['data']['forecast'][0]
return city, data['high'], data['low']
def show(w):
for x in w:
print(x)
w = WeatherIterable(['北京', '上海', '深圳'] * 3)
show(w)
结果:
('北京', '高温 33℃', '低温 20℃')
('上海', '高温 36℃', '低温 26℃')
('深圳', '高温 32℃', '低温 27℃')
('北京', '高温 33℃', '低温 20℃')
('上海', '高温 36℃', '低温 26℃')
('深圳', '高温 32℃', '低温 27℃')
('北京', '高温 33℃', '低温 20℃')
('上海', '高温 36℃', '低温 26℃')
('深圳', '高温 32℃', '低温 27℃')
通过生成器,不需要手动去维护迭代状态,生成器对象可以自动维护迭代状态,每次yield才返回一个气温信息。