Python并发编程之从生成器使用入门协程(七)
大家好,并发编程
进入第七篇。
从今天开始,我们将开始进入Python的难点,那就是协程
。
为了写明白协程的知识点,我查阅了网上的很多相关资料。发现很难有一个讲得系统,讲得全面的文章,导致我们在学习的时候,往往半知半解,学完还是一脸懵逼。
学习协程的第一门课程,是要认识生成器
,有了生成器
的基础,才能更好地理解协程
。
如果你是新手,那么你应该知道迭代器
,对生成器
应该是比较陌生的吧。没关系,看完这系列文章,你也能从小白成功过渡为Ptyhon高手。
再次提醒:
本系列所有的代码均在Python3下编写,也建议大家尽快投入到Python3的怀抱中来。
本文目录
- 可迭代、迭代器、生成器
- 如何运行/激活生成器
- 生成器的执行状态
- 生成器的异常处理
- 从生成器过渡到协程:yield
. 可迭代、迭代器、生成器
初学Python的时候,对于这三货真的是傻傻分不清。甚至还认为他们是等价的。
其实,他们是不一样的。
可迭代的对象,很好理解,我们很熟悉的:字符串
,list
,dict
,tuple
,deque
等
为了验证我说的,需要借助collections.abc
这个模块(Python2没有),使用isinstance()
来类别一个对象是否是可迭代的(Iterable
),是否是迭代器(Iterator
),是否是生成器(Generator
)。
import collections
from collections.abc import Iterable, Iterator, Generator
# 字符串
astr = "XiaoMing"
print("字符串:{}".format(astr))
print(isinstance(astr, Iterable))
print(isinstance(astr, Iterator))
print(isinstance(astr, Generator))
# 列表
alist = [21, 23, 32,19]
print("列表:{}".format(alist))
print(isinstance(alist, Iterable))
print(isinstance(alist, Iterator))
print(isinstance(alist, Generator))
# 字典
adict = {"name": "小明", "gender": "男", "age": 18}
print("字典:{}".format(adict))
print(isinstance(adict, Iterable))
print(isinstance(adict, Iterator))
print(isinstance(adict, Generator))
# deque
adeque=collections.deque('abcdefg')
print("deque:{}".format(adeque))
print(isinstance(adeque, Iterable))
print(isinstance(adeque, Iterator))
print(isinstance(adeque, Generator))
输出结果
字符串:XiaoMing
True
False
False
列表:[21, 23, 32, 19]
True
False
False
字典:{'name': '小明', 'gender': '男', 'age': 18}
True
False
False
deque:deque(['a', 'b', 'c', 'd', 'e', 'f', 'g'])
True
False
False
从结果来看,这些可迭代对象都不是迭代器,也不是生成器。它们有一个共同点,就是它们都可以使用for
来循环。这一点,大家都知道,我们就不去验证了。
扩展知识:
可迭代对象,是其内部实现了,__iter__
这个魔术方法。
可以通过,dir()
方法来查看是否有__iter__
来判断一个变量是否是可迭代的。
接下来是,迭代器
。
对比可迭代对象,迭代器
其实就只是多了一个函数而已。就是__next__()
,我们可以不再使用for
循环来间断获取元素值。而可以直接使用next()方法来实现。
迭代器,是在可迭代的基础上实现的。要创建一个迭代器,我们首先,得有一个可迭代对象。
现在就来看看,如何创建一个可迭代对象,并以可迭代对象为基础创建一个迭代器。
from collections.abc import Iterable, Iterator, Generator
class MyList(object): # 定义可迭代对象类
def __init__(self, num):
self.end = num # 上边界
# 返回一个实现了__iter__和__next__的迭代器类的实例
def __iter__(self):
return MyListIterator(self.end)
class MyListIterator(object): # 定义迭代器类
def __init__(self, end):
self.data = end # 上边界
self.start = 0
# 返回该对象的迭代器类的实例;因为自己就是迭代器,所以返回self
def __iter__(self):
return self
# 迭代器类必须实现的方法,若是Python2则是next()函数
def __next__(self):
while self.start < self.data:
self.start += 1
return self.start - 1
raise StopIteration
if __name__ == '__main__':
my_list = MyList(5) # 得到一个可迭代对象
print(isinstance(my_list, Iterable)) # True
print(isinstance(my_list, Iterator)) # False
# 迭代
for i in my_list:
print(i)
my_iterator = iter(my_list) # 得到一个迭代器
print(isinstance(my_iterator, Iterable)) # True
print(isinstance(my_iterator, Iterator)) # True
# 迭代
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
输出
0
1
2
3
4
True
False
True
True
0
1
2
3
4
如果上面的代码太多,也可以看这边,你更能理解。
from collections.abc import Iterator
aStr = 'abcd' # 创建字符串,它是可迭代对象
aIterator = iter(aStr) # 通过iter(),将可迭代对象转换为一个迭代器
print(isinstance(aIterator, Iterator)) # True
next(aIterator) # a
next(aIterator) # b
next(aIterator) # c
next(aIterator) # d
扩展知识:
迭代器,是其内部实现了,__next__
这个魔术方法。(Python3.x)
可以通过,dir()
方法来查看是否有__next__
来判断一个变量是否是迭代器的。
接下来,是我们的重点,生成器
。
生成器的概念在 Python 2.2 中首次出现,之所以引入生成器,是为了实现一个在计算下一个值时不需要浪费空间的结构。
前面我们说,迭代器,是在可迭代的基础上,加了一个next()方法。
而生成器,则是在迭代器的基础上(可以用for循环,可以使用next()
),再实现了yield
。
yield
是什么东西呢,它相当于我们函数里的return。在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。正是由于这个机制,才使用生成器在Python编程中大放异彩。实现节省内存,实现异步编程。
如何创建一个生成器,主要有如下两种方法
- 使用列表生成式
# 使用列表生成式,注意不是[],而是()
L = (x * x for x in range(10))
print(isinstance(L, Generator)) # True
- 实现yield的函数
# 实现了yield的函数
def mygen(n):
now = 0
while now < n:
yield now
now += 1
if __name__ == '__main__':
gen = mygen(10)
print(isinstance(gen, Generator)) # True
可迭代对象和迭代器,是将所有的值都生成存放在内存中,而生成器
则是需要元素才临时生成,节省时间,节省空间。
. 如何运行/激活生成器
由于生成器并不是一次生成所有元素,而是一次一次的执行返回,那么如何刺激生成器执行(或者说激活)呢?
激活主要有两个方法
- 使用
next()
- 使用
generator.send(None)
分别看下例子,你就知道了。
def mygen(n):
now = 0
while now < n:
yield now
now += 1
if __name__ == '__main__':
gen = mygen(4)
# 通过交替执行,来说明这两种方法是等价的。
print(gen.send(None))
print(next(gen))
print(gen.send(None))
print(next(gen))
输出
0
1
2
3
. 生成器的执行状态
生成器在其生命周期中,会有如下四个状态
GEN_CREATED
# 等待开始执行GEN_RUNNING
# 解释器正在执行(只有在多线程应用中才能看到这个状态)GEN_SUSPENDED
# 在yield表达式处暂停GEN_CLOSED
# 执行结束
通过代码来感受一下,为了不增加代码理解难度,GEN_RUNNING
这个状态,我就不举例了。有兴趣的同学,可以去尝试一下多线程。若有疑问,可在后台回复我。
from inspect import getgeneratorstate
def mygen(n):
now = 0
while now < n:
yield now
now += 1
if __name__ == '__main__':
gen = mygen(2)
print(getgeneratorstate(gen))
print(next(gen))
print(getgeneratorstate(gen))
print(next(gen))
gen.close() # 手动关闭/结束生成器
print(getgeneratorstate(gen))
输出
GEN_CREATED
0
GEN_SUSPENDED
1
GEN_CLOSED
. 生成器的异常处理
在生成器工作过程中,若生成器不满足生成元素的条件,就会
/应该
抛出异常(StopIteration
)。
通过列表生成式构建的生成器,其内部已经自动帮我们实现了抛出异常这一步。不信我们来看一下。
所以我们在自己定义一个生成器的时候,我们也应该在不满足生成元素条件的时候,抛出异常。
拿上面的代码来修改一下。
def mygen(n):
now = 0
while now < n:
yield now
now += 1
raise StopIteration
if __name__ == '__main__':
gen = mygen(2)
next(gen)
next(gen)
next(gen)
. 从生成器过渡到协程:yield
通过上面的介绍,我们知道生成器为我们引入了暂停函数执行(yield
)的功能。当有了暂停的功能之后,人们就想能不能在生成器暂停的时候向其发送一点东西(其实上面也有提及:send(None)
)。这种向暂停的生成器发送信息的功能通过 PEP 342
进入 Python 2.5
中,并催生了 Python
中协程
的诞生。根据 wikipedia
中的定义
协程是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序。
注意从本质上而言,协程并不属于语言中的概念,而是编程模型上的概念。
协程和线程,有相似点
,多个协程之间和线程一样,只会交叉串行执行;也有不同点
,线程之间要频繁进行切换,加锁,解锁,从复杂度和效率来看,和协程相比,这确是一个痛点。协程通过使用 yield
暂停生成器,可以将程序的执行流程交给其他的子程序,从而实现不同子程序的之间的交替执行。
下面通过一个简明的演示来看看,如何向生成器中发送消息。
def jumping_range(N):
index = 0
while index < N:
# 通过send()发送的信息将赋值给jump
jump = yield index
if jump is None:
jump = 1
index += jump
if __name__ == '__main__':
itr = jumping_range(5)
print(next(itr))
print(itr.send(2))
print(next(itr))
print(itr.send(-1))
输出。
0
2
3
2
这里解释下为什么这么输出。
重点是jump = yield index
这个语句。
分成两部分:
yield index
是将indexreturn
给外部调用程序。jump = yield
可以接收外部程序通过send()发送的信息,并赋值给jump
以上这些,都是讲协程并发的基础必备知识,请一定要亲自去实践并理解它,不然后面的内容,将会变得枯燥无味,晦涩难懂。
下一章,我将讲一个Python3.5新引入的语法:yield from
。篇幅也比较多,所以就单独拿出来讲。
好了,今天就讲这些。
Python并发编程之从生成器使用入门协程(七)的更多相关文章
- python并发编程之进程、线程、协程的调度原理(六)
进程.线程和协程的调度和运行原理总结. 系列文章 python并发编程之threading线程(一) python并发编程之multiprocessing进程(二) python并发编程之asynci ...
- py 并发编程(线程、进程、协程)
一.操作系统 操作系统是一个用来协调.管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用程序之间. 程序是运行在系统上的具有某种功能的软件,比如说浏览器,音乐播放器等.操作系统的内核的定义:操 ...
- day33_8_15 并发编程4,线程池与协程,io模型
一.线程池 线程池是一个处理线程任务的集合,他是可以接受一定量的线程任务,并创建线程,处理该任务,处理结束后不会立刻关闭池子,会继续等待提交的任务,也就是他们的进程/线程号不会改变. 当线程池中的任务 ...
- 并发编程 --进、线程池、协程、IO模型
内容目录: 1.socket服务端实现并发 2.进程池,线程池 3.协程 4.IO模型 1.socket服务端实现并发 # 客户端: import socket client = socket.soc ...
- Python并发编程之深入理解yield from语法(八)
大家好,并发编程 进入第八篇. 直到上一篇,我们终于迎来了Python并发编程中,最高级.最重要.当然也是最难的知识点--协程. 当你看到这一篇的时候,请确保你对生成器的知识,有一定的了解.当然不了解 ...
- Python 并发编程(下)
Python 并发编程(下) 课程目标:掌握多进程开发的相关知识点并初步认识协程. 今日概要: 多进程开发 进程之间数据共享 进程锁 进程池 协程 1. 多进程开发 进程是计算机中资源分配的最小单元: ...
- Python并发编程之初识异步IO框架:asyncio 上篇(九)
大家好,并发编程 进入第九篇. 通过前两节的铺垫(关于协程的使用),今天我们终于可以来介绍我们整个系列的重点 -- asyncio. asyncio是Python 3.4版本引入的标准库,直接内置了对 ...
- Python并发编程二(多线程、协程、IO模型)
1.python并发编程之多线程(理论) 1.1线程概念 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于 ...
- Python并发编程__多进程
Python并发编程_多进程 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大 ...
随机推荐
- FFMPEG结构体分析:AVFrame
注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrameFFMPEG结构体分析:AVFormatContextFFMPEG结构体分析:AVCodecContext ...
- 程序员编程艺术:第三章续、Top K算法问题的实现
程序员编程艺术:第三章续.Top K算法问题的实现 作者:July,zhouzhenren,yansha. 致谢:微软100题实现组,狂想曲创作组. 时间:2011年05月08日 ...
- 在SD/MMC卡上实现hive (Implement WinCE HIVE&ROM system on NAND or SD system )
本是个很简单的topic,但无奈的是很多客户都没有实现.所以只能写一个guide给客户,让他们依葫芦画瓢. 在SD卡上实现hive以及实现binfs最精髓的思想是,在boot stage 1依次加载s ...
- 安卓TV开发(前言)— AndroidTV模拟器初识与搭建
原文:http://blog.csdn.net/sk719887916/article/details/39612577skay 前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居 ...
- Libevent库学习笔记
Libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,Libevent在底层select.pool.kqueue和epoll等机制基础上,封装出一致的事件接口.可 ...
- 64位ubuntu安装N64模拟器mupen64
我们知道在windows平台下模拟器多如牛毛,N64的模拟器也不例外.而linux下对于想玩N64的童鞋们有啥好办法呢?我通过度娘找到一款开源的N64模拟器mupen64,其官方网址为:http:// ...
- ASP.NET Provider模式应用之SqlMembershipProvider类的剖析
太多了,先给个流程图吧 Provider模式就是GOF中的两种设计模式的应用:策略模式和工厂模式,在程序中使用好这个模型能够解除模块与模块之间的耦合甚至是DIP,同时,不管是ASP.NET MVC还是 ...
- <转>如何在iOS 7中设置barTintColor实现类似网易和 Facebook 的 navigationBar 效果
转自:i‘m Allen的博客 先给代码:https://github.com/allenhsu/CRNavigationController 1. 问题的表现 相信很多人在 iOS 7 的适配过程中 ...
- Next Permutation 下一个排列
Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...
- linux上安装redis的踩坑过程
redis用处很广泛,我不再啰嗦了,我按照网上教程想在linux上安装下,开始了踩坑过程,网上买了一个linux centos7.3,滴滴云的,巨坑无比啊,不建议大家用这家的! redis 为4.0, ...