本文已参与「新人创作礼」活动,一起开启掘金创作之路。
5.1,迭代器
迭代是访问集合元素的一种方式,迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有元素被访问结束,迭代器只往前不往后
-
可迭代对象
我们知道对list,tuple,str等数据类型的数据使用for...in...的循环语法能从其中依次拿到数据进行使用,我们把这样的过程叫做
遍历但是,是否所有的数据类型都能够被我们放到for...in...里面使用,给我们进行迭代?
In [1]: for i in 100: ...: print(i) ...: Traceback (most recent call last) ----> 1 for i in 100: 2 print(i) TypeError: 'int' object is not iterable # int整数不是iterable,即int整数不是可迭代的 # 我们可以自己定义一个容器MyList用来存放数据,可以通过add方法向其中添加数据查看一个对象是否是可迭代对象
-
isinstance(对象, Iterable)使用内置函数判断类型from collections import Iterable isinstance([1, 2, 3, 4], Iterable) True isinstance('dwadadwa', Iterable) True isinstance(123, Iterable) False -
dir(对象)查看一个对象是否实现了__iter__这个魔法方法dir([]) ['__add__','__class__','__contains__',...'__iter__',...'remove','reverse','sort']
-
-
for循环的实现过程
-
实现一个迭代器
# !/usr/bin/env python # _*_ coding:utf-8 _*_ # author:满怀心 2019/8/5 11:47 """ # code is far away from bugs with the god animal protecting I love animals. They taste delicious. ┏┓ ┏┓ ┏┛┻━━━┛┻┓ ┃ ☃ ┃ ┃ ┳┛ ┗┳ ┃ ┃ ┻ ┃ ┗━┓ ┏━┛ ┃ ┗━━━┓ ┃ 神兽保佑 ┣┓ ┃ 永无BUG! ┏┛ ┗┓┓┏━┳┓┏┛ ┃┫┫ ┃┫┫ ┗┻┛ ┗┻┛ """ from collections import Iterable, Iterator class Classmate(object): def __init__(self): self.names = list() def add(self, name): self.names.append(name) def __iter__(self): """如果想让一个对象变成一个可迭代对象,必须要实现__iter__方法""" return ClassIterator(self.names) class ClassIterator(object): def __init__(self, names): self.names = names self.index = 0 def __iter__(self): pass def __next__(self): if self.index < len(self.names): name = self.names[self.index] self.index += 1 return name else: raise StopIteration classmate = Classmate() classmate.add('one') classmate.add('two') classmate.add('three') for name in classmate: print(name)
-完整版迭代器
# !/usr/bin/env python
# _*_ coding:utf-8 _*_
# author:满怀心 2019/8/5 11:47
"""
# code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
┏┓ ┏┓
┏┛┻━━━┛┻┓
┃ ☃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保佑 ┣┓
┃ 永无BUG! ┏┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
"""
from collections import Iterable, Iterator
class Classmate(object):
def __init__(self):
self.names = list()
self.index = 0
def add(self, name):
self.names.append(name)
def __iter__(self):
"""如果想让一个对象变成一个可迭代对象,必须要实现__iter__方法"""
return self
def __next__(self):
if self.index < len(self.names):
name = self.names[self.index]
self.index += 1
return name
else:
raise StopIteration
classmate = Classmate()
classmate.add('one')
classmate.add('two')
classmate.add('three')
for name in classmate:
print(name)
5.2,迭代器的应用(重点)
如果要用到一堆值,有可能100个,10000个,说不定要用到,有两种方法生成
- 使用列表 :
保存生成的值(占用大量的内存空间)- 使用迭代器 :
保存生成值的方式(占用极小的空间实现)
如果要生成1000万个值呢?是在内存里面生成一个列表把这1000万个值存入,还是要用的时候再生成?
-
使用列表方式储存
nums = list() a = 0 b = 1 i = 0 while i < 10: nums.append(a) a, b = b, a+b i += 1 for num in nums: print(num) -
使用迭代器生成
class FeibIterator(object): def __init__(self, num): self.a = 0 self.b = 1 self.num = num self.current_num = 0 def __iter__(self): """如果想让一个对象变成一个可迭代对象,必须要实现__iter__方法""" return self def __next__(self): if self.current_num < self.num: ret = self.a self.a, self.b = self.b, self.a+self.b self.current_num += 1 return ret else: raise StopIteration a = FeibIterator(50) for i in a: print(i)迭代器的另外用途
除了for循环能够接受可迭代对象,list,tuple等也能接收
a = (1, 2, 3, 4, 5) list(a) b = [1, 2, 3, 4, 5] tuple(b) # 首先,先生成一个新的列表,再去找a的迭代器,把值一个一个迭代出来塞到列表里,最后捕捉异常
5.3,生成器
-
生成器
利用迭代器,我们可以每次使用next来取值,大量减少了内存空间,只暂用了生成数值的方法
但是我们在实现一个迭代器时,关于当前迭代到达的状态需要我们自己记录,进而才能根据当前状态生成下一个数据
为了达到记录当前的状态,我们采用更加简便的语法,即生成器,
生成器是一类特殊的迭代器 -
创建生成器方法1
-
创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表推导式的
[]改成()In [2]: l = [i for i in range(10)] In [3]: l Out[3]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] In [4]: L = (i for i in range(10)) In [5]: L Out[5]: <generator object <genexpr> at 0x7f73740b5620>创建 L 和 G 的的区别仅在于最外层的 [] 和 (),l 是一个列表,而 L 是一个生成器。我们可以直接打印出列表 L 的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数,for循环,list()等方法
-
-
创建生成器方法2
-
使用yield能够生成生成器,在函数里面使用
yield能够暂停函数,等待下次的next或者send方法来再次激活def func(num): a = 0 while a<num : yield a a += 1 # 这个函数使用了yield关键字,这是一个生成器函数,返回的对象是生成器对象 """ 1,返回这个函数对象 yield一个对象: 2,暂停这个函数 3,等待下次next重新激活函数 """ a = hello() print(a) print(type(a))
-
-
激活生成器方法
-
使用
next()或者__next__()激活def func(num): a = 0 while a<num : yield a a += 1 a = func() next(a) a.__next__() -
使用
send(传入的数据)激活,获得的值和next获得的值相同def func(): a = 0 while True: res = yield a # res接收send发送的值,如果用next调用激活,不会传值,为空 print('--send 发送的数据 : {}--'.format(res)) a += 1 a = func() num1 = next(a) # 激活生成器,接收生成器生成的值 print(num1) num2 = a.send('hahaha') # send不能放在开头,不然会报错,hahah的值会传给res print(num2)
-
5.4,使用yield完成多任务
抓住yield能够使函数暂停,直到下一次的next来激活,从而实现多任务
# !/usr/bin/env python
# _*_ coding:utf-8 _*_
# author:满怀心 2019/8/5 23:15
"""
# code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
┏┓ ┏┓
┏┛┻━━━┛┻┓
┃ ☃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保佑 ┣┓
┃ 永无BUG! ┏┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
"""
import time
def task_1():
while True:
print('---1---')
time.sleep(.1)
yield
def task_2():
while True:
print('---2---')
time.sleep(.1)
yield
def main():
t1 = task_1()
t2 = task_2()
while True:
next(t1)
next(t2)
if __name__ == '__main__':
main()
5.5,使用greenlet,gevent完成多任务
-
使用greenlet(了解)
from greenlet import greenlet import time def text1(): while True: print('-----A-----') gr2.switch() time.sleep(0.5) def text2(): while True: print('-----B-----') gr1.switch() time.sleep(0.5) gr1 = greenlet(text1) gr2 = greenlet(text2) gr1.switch() -
使用gevent(并发库)
使用gevent库执行的时候,
如果碰到延时,会自动切换,充分利用了延时时间去执行别的
-
gevent的基本使用
# !/usr/bin/env python # _*_ coding:utf-8 _*_ # author:满怀心 2019/8/6 12:23 """ # code is far away from bugs with the god animal protecting I love animals. They taste delicious. ┏┓ ┏┓ ┏┛┻━━━┛┻┓ ┃ ☃ ┃ ┃ ┳┛ ┗┳ ┃ ┃ ┻ ┃ ┗━┓ ┏━┛ ┃ ┗━━━┓ ┃ 神兽保佑 ┣┓ ┃ 永无BUG! ┏┛ ┗┓┓┏━┳┓┏┛ ┃┫┫ ┃┫┫ ┗┻┛ ┗┻┛ """ import gevent import time from gevent import monkey def func1(n): for i in range(n): print(gevent.getcurrent(), i) # time.sleep(1) # 碰到这种延时不会切换,必须是换成gevent里面对应的延时 gevent.sleep(1) # 主线程遇到sleep()发生延时,gevent自动切换 def func2(n): for i in range(n): print(gevent.getcurrent(), i) gevent.sleep(1) # 主线程遇到sleep()发生延时,gevent自动切换 def func3(n): for i in range(n): print(gevent.getcurrent(), i) gevent.sleep(1) # 主线程遇到sleep()发生延时,gevent自动切换 f1 = gevent.spawn(func1, 5) f2 = gevent.spawn(func2, 5) f3 = gevent.spawn(func3, 5) f1.join() # 主线程遇到join()发生延时,gevent自动切换 f2.join() f3.join()结果:
-
使用
monkey.patch_all()替换代码里的所有延时,实现协程多任务# !/usr/bin/env python # _*_ coding:utf-8 _*_ # author:满怀心 2019/8/6 12:23 """ # code is far away from bugs with the god animal protecting I love animals. They taste delicious. ┏┓ ┏┓ ┏┛┻━━━┛┻┓ ┃ ☃ ┃ ┃ ┳┛ ┗┳ ┃ ┃ ┻ ┃ ┗━┓ ┏━┛ ┃ ┗━━━┓ ┃ 神兽保佑 ┣┓ ┃ 永无BUG! ┏┛ ┗┓┓┏━┳┓┏┛ ┃┫┫ ┃┫┫ ┗┻┛ ┗┻┛ """ import gevent import time from gevent import monkey monkey.patch_all() def func(work_name): for i in range(10): print(work_name, i) time.sleep(random.random()) def main(): gevent.joinall( { gevent.spawn(func, 'work1'), gevent.spawn(func, 'work2'), gevent.spawn(func, 'work3'), } ) if __name__ == '__main__': start_time = time.time() main() print('一共执行{}时间'.format(time.time()-start_time)) # 一共执行17.179136753082275时间 不使用协程 # 一共执行6.5413126945495605时间 使用协程
5.6,并发图片下载器
利用在网络IO传输的过程中消耗的时间差来切换到另外一张图片的下载,节省时间
# !/usr/bin/env python
# _*_ coding:utf-8 _*_
# author:满怀心 2019/8/6 12:23
"""
# code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
┏┓ ┏┓
┏┛┻━━━┛┻┓
┃ ☃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保佑 ┣┓
┃ 永无BUG! ┏┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
"""
import gevent
from gevent import monkey
import requests
monkey.patch_all()
def download_img(img_name, img_url):
res = requests.get(img_url)
with open(r'{}'.format(img_name), 'wb') as f:
f.write(res.content)
def main():
gevent.joinall({
gevent.spawn(download_img, '1.jpg','https://i0.hdslb.com/bfs/sycp/creative_img/201908/ad72e7afe1c2e6e60bf4d219d1b5acf4.jpg@880w_440h.jpg'),
gevent.spawn(download_img, '2.jpg','https://i0.hdslb.com/bfs/archive/91d5d1c1922ed3f04a0c161a2a31aa5fba841819.jpg@160w_100h.jpg')
})
if __name__ == '__main__':
main()