九.迭代器、生成器与协程

一.迭代器

什么是迭代器?迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,它只能往前进行,不能后退。

什么是可迭代(可迭代对象)?

  • 1.遵循了可迭代协议的对象
  • 2.可迭代协议
    • 实现iter()方法,并返回的Iterator对象本身
    • 实现next()方法,当next()方法被调用,返回下一个值,直到没有值可以访问。

list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器,之后使用next()函数来获取下一条数据。iter()函数实际上就是调用了__iter_

In [5]: list_demo = [i for i in range(10)]
In [6]: iter_list = iter(list_de)
In [7]: iter_list = iter(list_demo)

In [8]: next(iter_list)
Out[8]: 0

In [9]: next(iter_list)
Out[9]: 1

In [10]: next(iter_list)
Out[10]: 2

二.生成器

什么是生成器?
我们使用列表时,列表是已经定义好的数据,例如[1, 2, 3, 4, 5, 6, 7, 8],这个可以依照某种算法推算出后续元素,是否可以不创建整个列表,而是遍历循环时,根据算法推算出元素然后使用呢,这样就可以节省大量空间,实现了这个需求的就是生成器(Generator),本质还是迭代器,但是特殊的迭代器。
生成器是一类特殊的迭代器。

生成器和普通函数的区别是使用yield,而不是return返回值。

  • 使用含推导式的()定义生成器
In [14]: list_demo = [i for i in range(10)]

In [15]: type(list_demo)
Out[15]: list

In [16]: generator_demo = (i for i in range(10))

In [17]: type(generator_demo)
Out[17]: generator

  • 使用函数来实现
def a():
    for x in range(3):
        print(x)
        yield

m = a()
for x in range(3):
    next(m)

协程

协程不是进程,也不是线程,它是用户空间调度的完成的并发处理的方式。

它是python中另外一种实现多任务的方式,只不过比线程更小,占用更小执行单元。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

协程是线程内完成调度,它不需要更多的线程,自然也没有多线程切换带来的开销。
协程是非抢占式调度,只有一个协程主动让出控制权,另一个协程才会被调度。
协程也不需要使用锁机制,因为是在同一个线程中执行。

  • 简单实现一个协程
def a():
    for x in range(3):
        print(x)
        yield
def b():
    for x in range(3):
        print(x)
        yield
m = a()
n = b()
for x in range(3):
    next(m)
    next(n)

在线程内通过生成器完成了调度,让两个函数几乎都有执行,这样的调度不是操作系统的进程、线程完成的,而是用户自己设计的,这个就是协程。

  • greenlet 实现协程
from greenlet import greenlet
import time

def test1():
    while True:
        print("---test1---")
        g2.switch()
        time.sleep(0.5)

def test2():
    while True:
        print("---test2---")
        g1.switch()
        time.sleep(0.5)

g1 = greenlet(test1)
g2 = greenlet(test2)

#切换到g1中运行
g1.switch()
  • gevent比greenlet更加强大,能够自动切换
    示例1
import gevent

def fun(n):
    for x in range(n):
        print(gevent.getcurrent(), x)
        gevent.sleep(1)

g1 = gevent.spawn(fun, 5)  # 第一个参数为传递的执行函数,第二个参数,是f的参数
g2 = gevent.spawn(fun, 5)
g3 = gevent.spawn(fun, 5)
g1.join()
g2.join()
g3.join()

示例2

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")
])

示例3

from gevent import monkey
import gevent
import random
import time
from gevent.pool import Pool

pool = Pool(5) # 限制发送协程数

monkey.patch_all()
#
def coroutine_work(coroutine_name):
    for i in range(10):
        print(coroutine_name, i)
        time.sleep(random.random())


gevent.joinall([
    pool.spawn(coroutine_work, coroutine_name='work1'),
    pool.spawn(coroutine_work, coroutine_name='work2'),
]
)
posted @ 2020-05-16 11:23  leafgood  阅读(200)  评论(0编辑  收藏  举报