1 可迭代对象和迭代器对象
Iterable,Iterator表示可迭代对象和迭代器对象。Iterator 从源码可以知道是继承了Iterable
from collections import Iterable,Iterator
l = [1,2,3,4]
for x in l: # 能放到 in 后的一定是一个可迭代的
print(x)
isinstance(l, Iterable) # True
isinstance(l, Iterator) # False
issubclass(list, Iterable) # True
issubclass(str, Iterable) # True
issubclass(dict, Iterable) # True
issubclass(int, Iterable) # False
iter(l)
l.__iter__() # 和上面一样,都是可迭代对象
it = iter(l)
next(it) #1
list(it) #[2, 3, 4] 因为可迭代对象是一次性消费
next(it) #StopIteration Traceback (most recent call last) 无就报错
it = iter(l)
isinstance(it ,Iterable) #True
isinstance(it ,Iterator) #True
实例:迭代气温
import requests
url = 'http://wthrcdn.etouch.cn/weather_mini?city='+'北京'
r = requests.get(url)
r.text # 文本形式显示
r.json() # json形式返回
r.text返回如下
'{"data":{"yesterday":{"date":"16日星期六","high":"高温 12℃","fx":"北风","low":"低温 1℃","fl":"<![CDATA[3级]]>","type":"晴"},"city":"北京","forecast":[{"date":"17日星期天","high":"高温 14℃","fengli":"<![CDATA[2级]]>","low":"低温 2℃","fengxiang":"西南风","type":"晴"},{"date":"18日星期一","high":"高温 15℃","fengli":"<![CDATA[2级]]>","low":"低温 4℃","fengxiang":"东北风","type":"多云"},{"date":"19日星期二","high":"高温 13℃","fengli":"<![CDATA[2级]]>","low":"低温 2℃","fengxiang":"东北风","type":"晴"},{"date":"20日星期三","high":"高温 15℃","fengli":"<![CDATA[1级]]>","low":"低温 3℃","fengxiang":"西南风","type":"晴"},{"date":"21日星期四","high":"高温 17℃","fengli":"<![CDATA[2级]]>","low":"低温 4℃","fengxiang":"西北风","type":"晴"}],"ganmao":"感冒易发期,外出请适当调整衣物,注意补充水分。","wendu":"14"},"status":1000,"desc":"OK"}'
from collections import Iterable,Iterator
import requests
class WeatherIterator(Iterator):
def __init__(self, cities):
self.cities = cities
self.index = 0
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 __next__(self):
if self.index == len(self.cities):
raise StopIteration
city = self.cities[self.index]
self.index += 1
return self.get_weather(city)
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)
w1 = WeatherIterable(['北京','合肥']*2)
show(w1)
print('-'*20)
show(w1)
w1 = WeatherIterator(['北京','上海','合肥']*2)
show(w1)
print('-'*20)
show(w1)
2 如何使用生成器函数实现可迭代对象
def f():
print('1')
yield 1
print('2')
yield 2
f() #<generator object f at 0x7f9e125b0450>
g = f()
from collections import Iterable,Iterator
isinstance(g, Iterable) #True
isinstance(g, Iterator) #True
iter(g) is g #True
next(g)
# 1
# 1
next(g)
# 2
# 2
next(g)
# StopIteration
实例:生成器创建可迭代对象
from collections import Iterable
class PrimeNumbers(Iterable):
def __init__(self, a, b):
self.a = a
self.b = b
def __iter__(self): # 将该类的 __iter__方法实现生成器函数,每次yield返回一个素数
for k in range(self.a, self.b + 1):
if self.is_prime(k):
yield k
def is_prime(self, k):
return False if k < 2 else all(map(lambda x: k % x, range(2, k)))
pn = PrimeNumbers(1, 30)
for n in pn:
print(n)
结果:
2
3
5
7
11
13
17
19
23
29
3 如何实现反向迭代
不建议的方式
l = [1,2,3,4,5]
l.reverse() # 会改变原来的list
l #[5, 4, 3, 2, 1]
l = [1,2,3,4,5]
l[::-1] # 效果同上,但是占用内存空间
建议使用
reversed(fr)
实例
class FloatRange:
def __init__(self, a, b, step):
self.a = a
self.b = b
self.step = step
def __iter__(self):
t = self.a
while t <= self.b:
yield t
t += self.step
def __reversed__(self):
t = self.b
while t >= self.a:
yield t
t -= self.step
fr = FloatRange(3.0, 4.0, 0.2)
for x in fr:
print(x)
print('-'*20)
for x in reversed(fr):
print(x)
结果:
3.0
3.2
3.4000000000000004
3.6000000000000005
3.8000000000000007
--------------------
4.0
3.8
3.5999999999999996
3.3999999999999995
3.1999999999999993
和自己预计的有差异,这是因为使用二进制转化产生的差异,并逐个放大。
from decimal import Decimal
class FloatRange:
def __init__(self, a, b, step):
self.a = Decimal(str(a))
self.b = Decimal(str(b))
self.step = Decimal(str(step))
def __iter__(self):
t = self.a
while t <= self.b:
yield float(t)
t += self.step
def __reversed__(self):
t = self.b
while t >= self.a:
yield float(t)
t -= self.step
fr = FloatRange(3.0, 4.0, 0.2)
for x in fr:
print(x)
print('-'*20)
for x in reversed(fr):
print(x)
3.0
3.2
3.4
3.6
3.8
4.0
--------------------
4.0
3.8
3.6
3.4
3.2
3.0
4 迭代器做切片操作
直接对f切片是不行的
f = open('t.txt')
f[2:3] #TypeError: '_io.TextIOWrapper' object is not subscriptable
可切片的本质
l = list(range(6))
l[2:4] #[2, 3] 切片操作实际是重载了这个函数:因此也可以写成 l.__getitem__(slice(2,4))
方法1:转成list
f = open('t.txt')
l = f.readlines()
# ['2020-12-1\n',
# '2020-12-2\n',
# '2020-12-3\n',
# '2020-12-4\n',
# '2020-12-5\n',
# '2020-12-6\n',
# '2020-12-7\n',
# '2020-12-8\n']
l[1:3] # ['2020-12-2\n', '2020-12-3\n']
方法2:自定义或使用函数
自定义
def myIslice(iterable, start, end, step=1):
tmp = 0
for i,x in enumerate(iterable):
if i >= end:
break
if i >= start:
if tmp == 0:
tmp = step
yield x
tmp -= 1
print(list(myIslice(range(100,130), 10, 20, 3))) #[110, 113, 116, 119]
print(list(myIslice(f, 1, 3, 1))) #['2020-12-2\n', '2020-12-3\n']
函数形式
from itertools import islice
print(list(islice(range(100,130), 10, 20, 3))) #[110, 113, 116, 119]
f = open('t.txt')
print(list(islice(f, 1, 3, 1))) #['2020-12-2\n', '2020-12-3\n']
5 for语句中迭代多个可迭代对象
并行
使用内置函数zip,可以将多个可迭代对象合并,每次迭代返回一个元组
实例:统计各每人各科总和
from random import randint
chinese = [randint(60, 100) for _ in range(20)]
math = [randint(60, 100) for _ in range(20)]
english= [randint(60, 100) for _ in range(20)]
#方式1
t = []
for s1, s2, s3 in zip(chinese, math, english):
t.append(s1 + s2 + s3)
#方式2
[sum(s) for s in zip(chinese, math, english)]
#方式3
list(map(sum, zip(chinese, math, english)))
#方式4
list(map(lambda s1,s2,s3: s1+s2+s3, chinese, math, english))
最后结果为列表的和形式。
list(map(lambda *args:args, chinese, math, english)) #和zip效果一样
串行
使用标准的库函数,itertools.chain,能将多个可迭代对象链接起来。
实例:统计数量大于90的人数
c1 = [randint(60, 100) for _ in range(20)]
c2 = [randint(60, 100) for _ in range(20)]
c3 = [randint(60, 100) for _ in range(20)]
c4 = [randint(60, 100) for _ in range(20)]
from itertools import chain
len([x for x in chain(c1,c2,c3,c4) if x > 90]) #19