1 可迭代对象和迭代器对象

Iterable,Iterator表示可迭代对象和迭代器对象。Iterator 从源码可以知道是继承了Iterable

  1. from collections import Iterable,Iterator
  2. l = [1,2,3,4]
  3. for x in l: # 能放到 in 后的一定是一个可迭代的
  4. print(x)
  1. isinstance(l, Iterable) # True
  2. isinstance(l, Iterator) # False
  3. issubclass(list, Iterable) # True
  4. issubclass(str, Iterable) # True
  5. issubclass(dict, Iterable) # True
  6. issubclass(int, Iterable) # False
  7. iter(l)
  8. l.__iter__() # 和上面一样,都是可迭代对象
  1. it = iter(l)
  2. next(it) #1
  3. list(it) #[2, 3, 4] 因为可迭代对象是一次性消费
  4. next(it) #StopIteration Traceback (most recent call last) 无就报错
  5. it = iter(l)
  6. isinstance(it ,Iterable) #True
  7. isinstance(it ,Iterator) #True

实例:迭代气温

  1. import requests
  2. url = 'http://wthrcdn.etouch.cn/weather_mini?city='+'北京'
  3. r = requests.get(url)
  4. r.text # 文本形式显示
  5. r.json() # json形式返回

r.text返回如下

  1. '{"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"}'
  1. from collections import Iterable,Iterator
  2. import requests
  3. class WeatherIterator(Iterator):
  4. def __init__(self, cities):
  5. self.cities = cities
  6. self.index = 0
  7. def get_weather(self, city):
  8. url = 'http://wthrcdn.etouch.cn/weather_mini?city=' + city
  9. r = requests.get(url)
  10. data = r.json()['data']['forecast'][0]
  11. return city,data['high'],data['low']
  12. def __next__(self):
  13. if self.index == len(self.cities):
  14. raise StopIteration
  15. city = self.cities[self.index]
  16. self.index += 1
  17. return self.get_weather(city)
  18. class WeatherIterable(Iterable):
  19. def __init__(self, cities):
  20. self.cities = cities
  21. def __iter__(self):
  22. return WeatherIterator(self.cities)
  23. def show(w):
  24. for x in w:
  25. print(x)
  26. w1 = WeatherIterable(['北京','合肥']*2)
  27. show(w1)
  28. print('-'*20)
  29. show(w1)

image.png

  1. w1 = WeatherIterator(['北京','上海','合肥']*2)
  2. show(w1)
  3. print('-'*20)
  4. show(w1)

由上可见迭代器对象是一次消费的。

2 如何使用生成器函数实现可迭代对象

  1. def f():
  2. print('1')
  3. yield 1
  4. print('2')
  5. yield 2
  6. f() #<generator object f at 0x7f9e125b0450>
  1. g = f()
  2. from collections import Iterable,Iterator
  3. isinstance(g, Iterable) #True
  4. isinstance(g, Iterator) #True
  5. iter(g) is g #True
  6. next(g)
  7. # 1
  8. # 1
  9. next(g)
  10. # 2
  11. # 2
  12. next(g)
  13. # StopIteration

实例:生成器创建可迭代对象

  1. from collections import Iterable
  2. class PrimeNumbers(Iterable):
  3. def __init__(self, a, b):
  4. self.a = a
  5. self.b = b
  6. def __iter__(self): # 将该类的 __iter__方法实现生成器函数,每次yield返回一个素数
  7. for k in range(self.a, self.b + 1):
  8. if self.is_prime(k):
  9. yield k
  10. def is_prime(self, k):
  11. return False if k < 2 else all(map(lambda x: k % x, range(2, k)))
  12. pn = PrimeNumbers(1, 30)
  13. for n in pn:
  14. print(n)

结果:

  1. 2
  2. 3
  3. 5
  4. 7
  5. 11
  6. 13
  7. 17
  8. 19
  9. 23
  10. 29

3 如何实现反向迭代

不建议的方式

  1. l = [1,2,3,4,5]
  2. l.reverse() # 会改变原来的list
  3. l #[5, 4, 3, 2, 1]
  4. l = [1,2,3,4,5]
  5. l[::-1] # 效果同上,但是占用内存空间

建议使用

  1. reversed(fr)

实例

  1. class FloatRange:
  2. def __init__(self, a, b, step):
  3. self.a = a
  4. self.b = b
  5. self.step = step
  6. def __iter__(self):
  7. t = self.a
  8. while t <= self.b:
  9. yield t
  10. t += self.step
  11. def __reversed__(self):
  12. t = self.b
  13. while t >= self.a:
  14. yield t
  15. t -= self.step
  16. fr = FloatRange(3.0, 4.0, 0.2)
  17. for x in fr:
  18. print(x)
  19. print('-'*20)
  20. for x in reversed(fr):
  21. print(x)

结果:

  1. 3.0
  2. 3.2
  3. 3.4000000000000004
  4. 3.6000000000000005
  5. 3.8000000000000007
  6. --------------------
  7. 4.0
  8. 3.8
  9. 3.5999999999999996
  10. 3.3999999999999995
  11. 3.1999999999999993

和自己预计的有差异,这是因为使用二进制转化产生的差异,并逐个放大。

  1. from decimal import Decimal
  2. class FloatRange:
  3. def __init__(self, a, b, step):
  4. self.a = Decimal(str(a))
  5. self.b = Decimal(str(b))
  6. self.step = Decimal(str(step))
  7. def __iter__(self):
  8. t = self.a
  9. while t <= self.b:
  10. yield float(t)
  11. t += self.step
  12. def __reversed__(self):
  13. t = self.b
  14. while t >= self.a:
  15. yield float(t)
  16. t -= self.step
  17. fr = FloatRange(3.0, 4.0, 0.2)
  18. for x in fr:
  19. print(x)
  20. print('-'*20)
  21. for x in reversed(fr):
  22. print(x)
  1. 3.0
  2. 3.2
  3. 3.4
  4. 3.6
  5. 3.8
  6. 4.0
  7. --------------------
  8. 4.0
  9. 3.8
  10. 3.6
  11. 3.4
  12. 3.2
  13. 3.0

4 迭代器做切片操作

直接对f切片是不行的

  1. f = open('t.txt')
  2. f[2:3] #TypeError: '_io.TextIOWrapper' object is not subscriptable

可切片的本质

  1. l = list(range(6))
  2. l[2:4] #[2, 3] 切片操作实际是重载了这个函数:因此也可以写成 l.__getitem__(slice(2,4))

方法1:转成list

  1. f = open('t.txt')
  2. l = f.readlines()
  3. # ['2020-12-1\n',
  4. # '2020-12-2\n',
  5. # '2020-12-3\n',
  6. # '2020-12-4\n',
  7. # '2020-12-5\n',
  8. # '2020-12-6\n',
  9. # '2020-12-7\n',
  10. # '2020-12-8\n']
  11. l[1:3] # ['2020-12-2\n', '2020-12-3\n']

方法2:自定义或使用函数

自定义

  1. def myIslice(iterable, start, end, step=1):
  2. tmp = 0
  3. for i,x in enumerate(iterable):
  4. if i >= end:
  5. break
  6. if i >= start:
  7. if tmp == 0:
  8. tmp = step
  9. yield x
  10. tmp -= 1
  11. print(list(myIslice(range(100,130), 10, 20, 3))) #[110, 113, 116, 119]
  12. 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