图片设计网站推荐,重庆网站制作天,网站开发流程详细介绍,采购合同文章目录 迭代器可迭代对象可迭代对象的本质iter()函数与 next()函数迭代器 Iterator样例 for...in...循环的本质使用的场景--斐波那契数列list和tuple也可以接收可迭代对象 生成器简介创建生成器方法一方法二总结 使用 send 唤醒 协程协程和线程差异简单实现协程greenletgeven… 文章目录 迭代器可迭代对象可迭代对象的本质iter()函数与 next()函数迭代器 Iterator样例 for...in...循环的本质使用的场景--斐波那契数列list和tuple也可以接收可迭代对象 生成器简介创建生成器方法一方法二总结 使用 send 唤醒 协程协程和线程差异简单实现协程greenletgevent安装gevent 的使用方法给程序打补丁gevent的常用方法 python最新接口最后的示例并行下载 迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象
我们已经知道可以对list、tuple、str 等类型的数据使用 for…in…的循环语法从其中依次拿到数据进行使用我们把这样的过程称为遍历也叫迭代。
我们把可以通过 for…in…这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象Iterable。
可以使用 isinstance() 判断该对象是否是一个可迭代的对象
In [50]: from collections.abc import Iterable
In [51]: isinstance([], Iterable)
Out[51]: True
In [52]: isinstance({}, Iterable)
Out[52]: True
In [53]: isinstance(abc, Iterable)
Out[53]: True
In [54]: isinstance(mylist, Iterable)
Out[54]: False
In [55]: isinstance(100, Iterable)
Out[55]: False可迭代对象的本质
可迭代对象通过__iter__方法向我们提供一个迭代器我们在迭代一个可迭代对 象的时候实际上就是先获取该对象提供的一个迭代器然后通过这个迭代器来依次获取对象中的每一个数据. 那么也就是说一个具备了__iter__方法的对象就是一个可迭代对象所以关键就在于我们要如何去重写这一个__iter__方法。
iter()函数与 next()函数
list、tuple 等都是可迭代对象我们可以通过 iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用 next()函数来获取下一条数据。
iter()函数实际上就是调用了可迭代对象的__iter__方法
注意当我们已经迭代完最后一个数据之后再次调用 next()函数会抛出StopIteration 的异常来告诉我们所有数据都已迭代完成不用再执行 next()函数了
迭代器 Iterator
通过上面的分析我们已经知道迭代器是用来帮助我们记录每次迭代访问到的位置当我们对迭代器使用 next()函数的时候迭代器会向我们返回它所记录位置的下一个位置的数据。实际上在使用 next()函数的时候调用的就是迭代器对象的__next__方法Python3 中是对象的__next__方法。所以我们要想构造一个迭代器就要实现它的__next__方法。但这还不够python 要求迭代器本身也是可迭代的所以我们还要为迭代器实现__iter__方法而__iter__方法要返回一个迭代器迭代器自身正是一个迭代器所以迭代器的__iter__方法返回自身即可。
一个实现了__iter__方法和__next__方法的对象就是迭代器。
样例
from collections.abc import Iterable
# 自定义可迭代对象
class MyList:def __init__(self):self.mylist []def add(self,num):self.mylist.append(num)# 此处是关键定义了这一个之后就相当于是让这个对象是可迭代对象def __iter__(self):return MyIterator(self)# 自定义迭代器
class MyIterator:def __init__(self,mylist):self.mylist:MyList mylistself.current 0def __iter__(self):return selfdef __next__(self):if self.current len(self.mylist.mylist) - 1:# 此处一定要实现抛出StopIteration异常raise StopIterationelse:current self.currentself.current self.current 1return self.mylist.mylist[current]if __name__ __main__:mylist MyList()mylist.add(1)mylist.add(2)mylist.add(3)for i in mylist:print(i)print(isinstance(mylist,Iterable))for…in…循环的本质
for item in Iterable 循环的本质就是先通过 iter()函数获取可迭代对象 Iterable 的迭代器然后对获取到的迭代器不断调用 next()方法来获取下一个值并将其赋值给item当遇到 StopIteration 的异常后循环
使用的场景–斐波那契数列 class FibIterator(object):斐波那契数列迭代器def __init__(self, n)::param n: int, 指明生成数列的前 n 个数self.n n# current 用来保存当前生成到数列中的第几个数了self.current 0# num1 用来保存前前一个数初始值为数列中的第一个数 0self.num1 0# num2 用来保存前一个数初始值为数列中的第二个数 1self.num2 1def __next__(self):被 next()函数调用来获取下一个数if self.current self.n:num self.num1self.num1, self.num2 self.num2, self.num1self.num2self.current 1return numelse:raise StopIterationdef __iter__(self):迭代器的__iter__返回自身即可return selfif __name__ __main__:fib FibIterator(10)for num in fib:print(num, end )list和tuple也可以接收可迭代对象
除了 for 循环能接收可迭代对象list、tuple 等也能接收
li list(FibIterator(15))
print(li)
tp tuple(FibIterator(6))
print(tp)
生成器
简介
利用迭代器我们可以在每次迭代获取数据通过 next()方法时按照特定的规律进行生成。但是我们在实现一个迭代器时关于当前迭代到的状态需要我们自己记录进而才能根据当前状态生成下一个数据。为了达到记录当前状态并配合next()函数进行迭代使用我们可以采用更简便的语法即生成器(generator)。生成器是一类特殊的迭代器。
创建生成器
方法一
实际上就是把一个列表生成式的 [ ] 改成 (
my_generator ( x*2 for x in range(5))
print(my_generator)
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))
print(next(my_generator))对于生成器来说我们可以按照迭代器的使用方法来使用即可以通过 next()函数、for 循环、list()等方法进行迭代
方法二
generator 非常强大。如果推算的算法比较复杂用类似列表生成式的 for 循环无法实现的时候还可以用函数来实现。
在使用生成器实现的方式中我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现但是将每次迭代返回数值的 return 换成了 yield此时新定义的函数便不再是函数而是一个生成器了。简单来说只要在 def 中有yield 关键字的 就称为 生成器此时按照调用函数的方式( 案例中为 F fib(5) )使用生成器就不再是执行函数体了而是会返回一个生成器对象 案例中为 F 然后就可以按照使用迭代器的方式来使用生成器了。
# 含有yield的函数称为生成器
def fib(n):current 0num1, num2 0, 1while current n:num num1num1, num2 num2, num1 num2current 1# print(num,end )yield numreturn done# F是一个生成器支持next
Ffib(10)print(F)# for i in F:
# print(i,end )# 迭代生成
l[ i for i in F ]
print(l)# 想要拿取return当中的值
try:next(F)
except StopIteration as e:print(\n返回值为:{}.format(e.value))用 for 循环调用 generator 时发现拿不到 generator 的 return 语句的返回 值。如果想要拿到返回值必须捕获 StopIteration 错误返回值包含在 StopIteration 的 value 中
这里还有一个小细节读者可能回想为什么我们在使用已经做好的list时候对于list进行for in处理可以多次处理多次拿值但是对于我们上文写的迭代器为什么不行原因就在于我们实际上list叫做可迭代对象它实际上所采用的是我们最上面的那种手法定义两个类的手法每次迭代都会有一个新的迭代器来运行。
总结
使用了 yield 关键字的函数不再是函数而是生成器。使用了 yield 的函数 就是生成器yield 关键字有两点作用 – 保存当前运行状态断点然后暂停执行即将生成器函数挂起 – 将 yield 关键字后面表达式的值作为返回值返回此时可以理解为起到了 return 的作用可以使用 next()函数让生成器从断点处继续执行即唤醒生成器函数Python3 中的生成器可以使用 return 返回最终运行的返回值
使用 send 唤醒
我们除了可以使用 next()函数来唤醒生成器继续执行外还可以使用 send()函数来唤醒执行。使用 send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。
def gen():i0while i5:tempyield iprint(temp)i1ggen()next(g)
g.send(hello)协程
协程又称微线程纤程。英文名 Coroutine。
协程是 python 个中另外一种实现多任务的方式只不过比线程更小占用更小执行单元理解为需要的资源。 为啥说它是一个执行单元因为它自带 CPU 上下文。这样只要在合适的时机 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU 上下文那么程序还是可以运行的。
通俗的理解在一个线程中的某个函数可以在任何地方保存当前函数的一些临时变量等信息然后切换到另外一个函数中执行注意不是通过调用函数的方式做到的并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。
协程和线程差异
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU 上下文这么简单。操作系统为了程序运行的高效性每个线程都有自己缓存 Cache 等等数据操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作 CPU 的上下文所以一秒钟切换个上百万次系统都抗的住。
简单实现协程
import timedef work1():while True:print(----work1---)yieldtime.sleep(0.5)def work2():while True:print(----work2---)yieldtime.sleep(0.5)def main():w1 work1()w2 work2()while True:next(w1)next(w2)if __name__ __main__:main()greenlet
基本的使用方法
from greenlet import greenlet
import timedef test1():while True:print(---A--)gr2.switch()time.sleep(0.5)def test2():while True:print(---B--)gr1.switch()time.sleep(0.5)gr1 greenlet(test1)
gr2 greenlet(test2)
#切换到 gr1 中运行
gr1.switch()
gevent
greenlet 已经实现了协程但是这个还的人工切换是不是觉得太麻烦了python 还有一个比 greenlet 更强大的并且能够自动切换任务的模块 gevent其原理是当一个 greenlet 遇到 IO(指的是 input output 输入输出比如网络、文件操作等)操作时比如访问网络就自动切换到其他的 greenlet等到 IO 操作完成再在适当的时候切换回来继续执行。
由于 IO 操作非常耗时经常使程序处于等待状态有了 gevent 为我们自动切换协程就保证总有协程在运行而不是等待 IO时间
参考链接
安装
pip install gevent -i https://pypi.tuna.tsinghua.edu.cn/simple
gevent 的使用方法
gevent.spawn 接口使用方法gevent.spawn(函数名传参)import geventdef f(n):for i in range(n):print(gevent.getcurrent(), i)#用来模拟一个耗时操作注意不是 time 模块中的 sleepgevent.sleep(1)g1 gevent.spawn(f, 5)
g2 gevent.spawn(f, 5)
g3 gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()运行结果
Greenlet at 0x7fa70ffa1c30: f(5) 0
Greenlet at 0x7fa70ffa1870: f(5) 0
Greenlet at 0x7fa70ffa1eb0: f(5) 0
Greenlet at 0x7fa70ffa1c30: f(5) 1
Greenlet at 0x7fa70ffa1870: f(5) 1
Greenlet at 0x7fa70ffa1eb0: f(5) 1
Greenlet at 0x7fa70ffa1c30: f(5) 2
Greenlet at 0x7fa70ffa1870: f(5) 2
Greenlet at 0x7fa70ffa1eb0: f(5) 2
Greenlet at 0x7fa70ffa1c30: f(5) 3
Greenlet at 0x7fa70ffa1870: f(5) 3
Greenlet at 0x7fa70ffa1eb0: f(5) 3
Greenlet at 0x7fa70ffa1c30: f(5) 4
Greenlet at 0x7fa70ffa1870: f(5) 4
Greenlet at 0x7fa70ffa1eb0: f(5) 4
给程序打补丁
猴子补丁作用 monkey patch 指的是在执行时动态替换,通常是在 startup 的时候. 用过 gevent 就会知道,会在最开头的地方 gevent.monkey.patch_all();把标准库中的 thread/socket 等给替换掉.这样我们在后面使用 socket的时候能够跟寻常一样使用,无需改动不论什么代码,可是它变成非堵塞的了
from gevent import monkey
import gevent
import random
import time# 这句话是关键
monkey.patch_all()def coroutine_work(coroutine_name):for i in range(10):print(coroutine_name, i)time.sleep(random.random())gevent.joinall([gevent.spawn(coroutine_work, work1),gevent.spawn(coroutine_work, work2)
])gevent的常用方法
常用方法说明gevent.spawn()创建一个普通的 Greenlet 对象并切换gevent.spawn_later(seconds3)延时创建一个普通的 Greenlet 对象并切换gevent.spawn_raw()创建的协程对象属于一个组gevent.getcurrent()返回当前正在执行的 greenletgevent.joinall(jobs)将协程任务添加到事件循环接收一个任务列表gevent.wait()可以替代 join 函数等待循环结束也可以传入协程对象列表gevent.kill()杀死一个协程gevent.killall()杀死一个协程列表里的所有协程monkey.patch_all()非常重要会自动将 python 的一些标准模块替换成 gevent框架
python最新接口
链接
官方文档 链接 链接
最后的示例
并行下载
from gevent import monkey
import gevent
import urllib.request# 有耗时操作时需要
monkey.patch_all()def my_downLoad(url):print(GET: %s % url)resp urllib.request.urlopen(url)data resp.read()print(%d bytes received from %s. % (len(data), url))gevent.joinall([gevent.spawn(my_downLoad, http://www.baidu.com/),gevent.spawn(my_downLoad, http://www.cskaoyan.com/),gevent.spawn(my_downLoad, http://www.qq.com/),
])以及
from gevent import monkey
import gevent
import urllib.request#有 IO 才做时需要这一句
monkey.patch_all()def my_downLoad(file_name, url):print(GET: %s % url)resp urllib.request.urlopen(url)data resp.read()with open(file_name, wb) as f:f.write(data)print(%d bytes received from %s. % (len(data), url))gevent.joinall([gevent.spawn(my_downLoad,7a082c0dde36eac2205a088397aaf295.jpg,http://qzs.qq.com/qzone/v6/v6_config/upload/7a082c0dde36eac2205a088397aaf295.jpg),gevent.spawn(my_downLoad,da8e974dc_is.jpg,https://pic1.zhimg.com/da8e974dc_is.jpg),])
上面的 url 可以换为自己需要下载视频、音乐、图片等url