python多任务之协程

迭代器

在python中常见的迭代方式实例
在这里插入图片描述

类创建的对象是否可以迭代呢,可以看下面的代码
代码示例

class Classmate(object):
	def __init__(self):
		self.names = list()

	def add(self, name):
		self.names.append(name)


classmate = Classmate()

classmate.add("张三")
classmate.add("李四")
classmate.add("王五")

for name in classmate:
	print(name)

运行后可以看到结果报错,不能迭代
在这里插入图片描述
在python中通过类创建的对象必须拥有__iter__方法,并且该方法返回的对象,必须要拥有__iter__方法和__next__方法才可以进行迭代。
其迭代流程如下:
1.在进行迭代之前,会进行判断该对象是否可以迭代,简单理解就是是否拥有__iter__方法。
2.在第一步成立的情况下,会调用iter函数得到该对象的__iter__的返回值,该返回值为一个迭代器。
3.第二步的返回的对象拥有__iter__和__next__方法,迭代的结果为__next__方法的返回值

将上面的代码改造一下

class Classmate(object):
	def __init__(self):
		self.names = list()

	def add(self, name):
		self.names.append(name)

	def __iter__(self):
		return ClassIterator()


class ClassIterator(object):
	def __iter__(self):
		pass

	def __next__(self):
		return 123


classmate = Classmate()

classmate.add("张三")
classmate.add("李四")
classmate.add("王五")

for name in classmate:
	print(name)

代码执行结果可以看到,能迭代但是所返回的值和我们要的值不一样,那是因为返回的是__next__方法的返回值
在这里插入图片描述
现在把代码再完善一下:

class Classmate(object):
	def __init__(self):
		self.names = list()

	def add(self, name):
		self.names.append(name)

	def __iter__(self):
		return ClassIterator(self)


class ClassIterator(object):
	def __init__(self, obj):
		self.obj =obj
		self.num = 0

	def __iter__(self):
		pass

	def __next__(self):
		if self.num < len(self.obj.names):
			ret = self.obj.names[self.num]
			self.num += 1
			return ret
		else:
			raise StopIteration

classmate = Classmate()

classmate.add("张三")
classmate.add("李四")
classmate.add("王五")

for name in classmate:
	print(name)

代码执行:
可以代码执行达到了想要的效果,上面代码改造增加了一个判断值,当列表取值取完了,代码并不会停下,会返回None,所有加了个返回异常,当迭代收到这个异常时就会停止迭代
在这里插入图片描述
上面的代码还可以完善一下,这样写两个类感觉有点多,可以合成以一个。

class Classmate(object):
	def __init__(self):
		self.names = list()
		self.num = 0

	def add(self, name):
		self.names.append(name)

	def __iter__(self):
		return self

	def __next__(self):
		if self.num < len(self.names):
			ret = self.names[self.num]
			self.num += 1
			return ret
		else:
			raise StopIteration

classmate = Classmate()

classmate.add("张三")
classmate.add("李四")
classmate.add("王五")

for name in classmate:
	print(name)

执行结果:
在这里插入图片描述
说了这么多,说下迭代器的优势,开头迭代字符串“python”这个是常见的方式,当字符串过小时,占用不了多少内存空间,假如需要迭代的字符串大到内存都无法加载的时候,这个时候使用fox循环进行迭代就会出问题,而说迭代器它是生成数据的一个方式,只会占用很少的内存空间。

生成器

以斐波那契数列数列来示例介绍
在这里插入图片描述可以写个简单的代码

def fibonacci(num):
	a, b = 0, 1
	current_num = 0
	while current_num < num:
		print(a)
		a, b = b, a+b
		current_num += 1


fibonacci(10)

执行效果
在这里插入图片描述
将代码改造成生成器的样子

def fibonacci(num):
	a, b = 0, 1
	current_num = 0
	while current_num < num:
		#print(a)
		yield a  
		a, b = b, a+b
		current_num += 1


obj = fibonacci(10)
for num in obj:
	print(num)

如果一个函数中有yield语句, 那么这个就不再是函数,而是一个生成器模板,在调用模板时需要创建对象。
在这里插入图片描述
因为生成器是特殊的迭代器,就可以通过next方法来获得参数

def fibonacci(num):
	print("----1----")
	a, b = 0, 1
	current_num = 0
	while current_num < num:
		print("----2----")
		#print(a)
		yield a 
		print("----3----") 
		a, b = b, a+b
		current_num += 1
		print("----4----")


obj = fibonacci(10)

ret = next(obj)
print(ret)

ret = next(obj)
print(ret)

通过增加注释的方式来执行代码可以看到生成器工作的过程,当返回第一个值的时候,代码执行到yield a 时返回a的值,当要返回第二个值时,代码执行会从yield a 往下执行到yield a 返回a的值
在这里插入图片描述

执行多任务

通过生成器的执行方式,就可以通过yield 方式执行多任务

import time


def test1():
	while True:
		print("----1----")
		time.sleep(1)
		yield


def test2():
	while True:
		print("----2---")
		time.sleep(1)
		yield


def main():
	y1 = test1()
	y2 = test2()
	while True:
		next(y1)
		next(y2)


if __name__ == '__main__':
	main()

结果可以看到两个函数同时执行完成多任务
在这里插入图片描述
但是一般协程都不使用yield而是使用gevent方式,使用gevent,当遇到延时就会执行下一个函数,所以要设置延时或者本身要有延时(阻塞)

import time
import gevent


def test1():
	while True:
		print("----1----")
		#time.sleep(1)
		gevent.sleep(1)


def test2():
	while True:
		print("----2---")
		#time.sleep(1)
		gevent.sleep(1)
		

def main():
	g1 = gevent.spawn(test1)
	g2 = gevent.spawn(test2)

	g1.join()
	g2.join()


if __name__ == '__main__':
	main()

执行效果如下,有几点要注意的
1.代码和之前线程,进程一样,传递参数的时候后面不是元组
2.gevent所有延时的方法都必须使用gevent.xxx比如time.sleep要写成gevent.sleep
3.当多任务过多时结尾可以写成

gevent.joinall([
	gevent.spawn(text1),
	gevent.spawn(text2)	
])

在这里插入图片描述

线程,进程,协程对比

1.进程是资源分配的单位
2.线程是操作系统调度的单位
3.进程切换需要的资源最大,效率很低
4.线程切换需要的资源一般,效率一般(不考虑gil情况下)
5.协程切换任务资源很少,效率高
6.多进程、多线程根据cpu核数不一样可能是并行的,但协程是在一个线程中所有是并发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值