python-多任务之协程

迭代的概念

使用for循环遍历取值的过程叫做迭代,比如:使用for循环遍历列表获取值的过程
# 例如
for value in [2, 3, 4]:
    print(value)

可迭代对象

使用for循环遍历取值的对象叫做可迭代对象, 比如:列表、元组、字典、集合、range、字符串

如何判断是否为可迭代对象

# 元组,列表,字典,字符串,集合,range都是可迭代对象
from collections.abc import Iterable
# 3.7以上版本为导入collections.abc
# from collections import Iterable

# 判断对象是否是指定类型
result = isinstance((3, 5), Iterable)
print("元组是否是可迭代对象:", result)

result = isinstance([3, 5], Iterable)
print("列表是否是可迭代对象:", result)

result = isinstance({"name": "张三"}, Iterable)
print("字典是否是可迭代对象:", result)

result = isinstance("hello", Iterable)
print("字符串是否是可迭代对象:", result)

result = isinstance({3, 5}, Iterable)
print("集合是否是可迭代对象:", result)

result = isinstance(range(5), Iterable)
print("range是否是可迭代对象:", result)

result = isinstance(5, Iterable)
print("整数是否是可迭代对象:", result)

# 提示: 以后还根据对象判断是否是其它类型,比如以后可以判断函数里面的参数是否是自己想要的类型
result = isinstance(5, int)
print("整数是否是int类型对象:", result)

class Student(object):
    pass

stu = Student()
result = isinstance(stu, Iterable)
print("stu是否是可迭代对象:", result)
result = isinstance(stu, Student)
print("stu是否是Student类型的对象:", result)

自定义可迭代对象

自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
from collections.abc import Iterable

# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):

    def __init__(self):
        self.my_list = list()

    # 添加指定元素
    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
        pass

my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
result = isinstance(my_list, Iterable)

print(result)

for value in my_list:
    print(value)
执行结果
Traceback (most recent call last):
True
  File "/home/python/Desktop/MyIterable.py", line 24, in <module>
    for value in my_list:
TypeError: iter() returned non-iterator of type 'NoneType'

自定义迭代器对象

from collections.abc import Iterable
from collections.abc import Iterator

# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):

    def __init__(self):
        self.my_list = list()

    # 添加指定元素
    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
        my_iterator = MyIterator(self.my_list)
        return my_iterator


# 自定义迭代器对象: 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象
class MyIterator(object):

    def __init__(self, my_list):
        self.my_list = my_list

        # 记录当前获取数据的下标
        self.current_index = 0

        # 判断当前对象是否是迭代器
        result = isinstance(self, Iterator)
        print("MyIterator创建的对象是否是迭代器:", result)

    def __iter__(self):
        return self

    # 获取迭代器中下一个值
    def __next__(self):
        if self.current_index < len(self.my_list):
            self.current_index += 1
            return self.my_list[self.current_index - 1]
        else:
            # 数据取完了,需要抛出一个停止迭代的异常
            raise StopIteration


my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
result = isinstance(my_list, Iterable)

print(result)

for value in my_list:
    print(value)
执行结果:
True
MyIterator创建的对象是否是迭代器: True
1
2
iter函数: 获取可迭代对象的迭代器,会调用可迭代对象身上的__iter__方法
next函数: 获取迭代器中下一个值,会调用迭代器对象身上的__next__方法
# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):

    def __init__(self):
        self.my_list = list()

    # 添加指定元素
    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
        my_iterator = MyIterator(self.my_list)
        return my_iterator


# 自定义迭代器对象: 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象
# 迭代器是记录当前数据的位置以便获取下一个位置的值
class MyIterator(object):

    def __init__(self, my_list):
        self.my_list = my_list

        # 记录当前获取数据的下标
        self.current_index = 0

    def __iter__(self):
        return self

    # 获取迭代器中下一个值
    def __next__(self):
        if self.current_index < len(self.my_list):
            self.current_index += 1
            return self.my_list[self.current_index - 1]
        else:
            # 数据取完了,需要抛出一个停止迭代的异常
            raise StopIteration

# 创建了一个自定义的可迭代对象
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)

# 获取可迭代对象的迭代器
my_iterator = iter(my_list)
print(my_iterator)
# 获取迭代器中下一个值
# value = next(my_iterator)
# print(value)

# 循环通过迭代器获取数据
while True:
    try:
        value = next(my_iterator)
        print(value)
    except StopIteration as e:
        break

for循环的本质

遍历的是可迭代对象
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
遍历的是迭代器
for item in Iterator 循环的迭代器,不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
from collections.abc import Iterable
from collections.abc import Iterator
'''
判断是否是可迭代对象:
1.0:能用for循环遍历的对象
2.0:可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
'''

'''
可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。
可迭代对象通过__iter__方法向我们提供一个迭代器,
我们在迭代一个可迭代对象的时候,
实际上就是先获取该对象提供的一个迭代器,
然后通过这个迭代器来依次获取对象中的每一个数据.
一个类中只要具有__iter__(self)方法,那么该对象就是一个可迭代对象
'''
class myList(object):

    def __init__(self,n):

        self.n = n

    def __iter__(self):

        return MyIterable(self.n)



class MyIterable(object):

    def __init__(self,n):
        self.n = n
        self.current = 0


    def __iter__(self):

        return self

    def __next__(self):

        if self.current<self.n:
            num = self.current
            self.current+=1
            return num
        else:
            raise StopIteration

# test = MyIterable(20)
test = myList(20)
print(isinstance(test,Iterable))
print(isinstance(test,Iterator))
print(isinstance(MyIterable(10),Iterator))
#   list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。
#   然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。
#   iter()函数实际上就是调用了可迭代对象的__iter__方法
# thisIterable = myList()
# print(isinstance([],Iterable))
# print(isinstance([],Iterator))
# print(isinstance(thisIterable,Iterable))
# print(isinstance(thisIterable,Iterator))
# print(isinstance(MyIterable,Iterable))
# print(isinstance(MyIterable,Iterator))
# li = [ i for i in range(5)]
# print(type(li))
# my_list = iter(li)
# print(next(my_list))

生成器的创建方法

# 第一种方法:
# test = (i for i in range(10))
# print(type(test))
# 在def函数里面看到有yield关键字那么就是生成器
# 第二种方法
def myIterable(n):
    current = 0
    while current < n:
        num = current
        current += 1
        yield num

test = myIterable(5)
for i in range(5):
    print(next(test))

def fibonacci(num):
    a = 0
    b = 1
    # 记录生成fibonacci数字的下标
    current_index = 0
    print("--11---")
    while current_index < num:
        result = a
        a, b = b, a + b
        current_index += 1
        print("--22---")
        # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
        yield result
        print("--33---")


fib = fibonacci(5)
value = next(fib)
print(value)
value = next(fib)
print(value)

value = next(fib)
print(value)

# for value in fib:
#     print(value)
# 在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返# 回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字# 的 就称为 生成器

生成器使用return 关键字

def fibonacci(num):
    a = 0
    b = 1
    # 记录生成fibonacci数字的下标
    current_index = 0
    print("--11---")
    while current_index < num:
        result = a
        a, b = b, a + b
        current_index += 1
        print("--22---")
        # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
        yield result
        print("--33---")
        return "嘻嘻"

fib = fibonacci(5)
value = next(fib)
print(value)
# 提示: 生成器里面使用return关键字语法上没有问题,但是代码执行到return语句会停止迭代,抛出停止迭代异常
# 在python3里面可以使用return关键字,python2不支持

# return 和 yield的区别
# yield: 每次启动生成器都会返回一个值,多次启动可以返回多个值,也就是yield可以返回多个值
# return: 只能返回一次值,代码执行到return语句就停止迭代

try:
    value = next(fib)
    print(value)
except StopIteration as e:
    # 获取return的返回值
    print(e.value)
# 生成器里面使用return关键字语法上没有问题,但是代码执行到return语句会停止迭代,抛出停止迭代异常
# 在python3里面可以使用return关键字,python2不支持

yield和return的对比

使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
每次启动生成器都会返回一个值,多次启动可以返回多个值,也就是yield可以返回多个值
return只能返回一次值,代码执行到return语句就停止迭代,抛出停止迭代异常

简单协程的实现

import time

def work1():
    while True:
        print("----work1---")
        yield
        time.sleep(0.5)

def work2():
    while True:
        print("----work2---")
        yield
        time.sleep(0.5)

def main():
    w1 = work1()
    w2 = work2()
    while True:
        next(w1)
        next(w2)

if __name__ == "__main__":
    main()

greenlet库的使用

import time
import greenlet


# 任务1
def work1():
    for i in range(5):
        print("work1...")
        time.sleep(0.2)
        # 切换到协程2里面执行对应的任务
        g2.switch()


# 任务2
def work2():
    for i in range(5):
        print("work2...")
        time.sleep(0.2)
        # 切换到第一个协程执行对应的任务
        g1.switch()


if __name__ == '__main__':
    # 创建协程指定对应的任务
    g1 = greenlet.greenlet(work1)
    g2 = greenlet.greenlet(work2)

    # 切换到第一个协程执行对应的任务
    g1.switch()

gevent----强大的异步并发库

greenlet已经实现了协程,但是这个还要人工切换,这里介绍一个比greenlet更强大而且能够自动切换任务的第三方库,那就是gevent。

gevent内部封装的greenlet,其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。

由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
import gevent

def work(n):
    for i in range(n):
        # 获取当前协程
        print(gevent.getcurrent(), i)

g1 = gevent.spawn(work, 5)
g2 = gevent.spawn(work, 5)
g3 = gevent.spawn(work, 5)
g1.join()
g2.join()
g3.join()
import gevent

def work(n):
    for i in range(n):
        # 获取当前协程
        print(gevent.getcurrent(), i)
        #用来模拟一个耗时操作,注意不是time模块中的sleep
        gevent.sleep(1)

g1 = gevent.spawn(work, 5)
g2 = gevent.spawn(work, 5)
g3 = gevent.spawn(work, 5)
g1.join()
g2.join()
g3.join()
import gevent
import time
from gevent import monkey

# 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时
monkey.patch_all()


# 任务1
def work1(num):
    for i in range(num):
        print("work1....")
        time.sleep(0.2)
        # gevent.sleep(0.2)

# 任务1
def work2(num):
    for i in range(num):
        print("work2....")
        time.sleep(0.2)
        # gevent.sleep(0.2)



if __name__ == '__main__':
    # 创建协程指定对应的任务
    g1 = gevent.spawn(work1, 3)
    g2 = gevent.spawn(work2, 3)

    # 主线程等待协程执行完成以后程序再退出
    g1.join()
    g2.join()

进程,线程,协程的区别

进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源最大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
进程、线程、协程都是可以完成多任务的,可以根据自己实际开发的需要选择使用
由于线程、协程需要的资源很少,所以使用线程和协程的几率最大
开辟协程需要的资源最少
发布了71 篇原创文章 · 获赞 1 · 访问量 1022

猜你喜欢

转载自blog.csdn.net/weixin_42917352/article/details/104145470