迭代器--》生成器--》协程的关系与区别

1、迭代器(iterator)

是一个实现了迭代器协议的对象,python的一些内置数据类型(列表,数组,字符串,字典等)都可以通过for语句进行迭代,我们也可以自己创建一个容器,实现了迭代器协议,可以通过for,next方法进行迭代,在迭代的末尾,会引发stopIteration异常。

判断xxx_obj是否可以迭代

在第1步成立的前提下,调用 iter 函数得到 xxx_obj 对象的 __iter__ 方法的返回值

__iter__ 方法的返回值是一个迭代器

如果想要一个对象称为一个 可以迭代的对象,即可以使用for,必须实现 __iter__方法

__iter__ 中必须返回对象的引用【要这个对象有__iter__和__next__方法, 实际上取的__next__的返回值】

迭代器结束,需要抛出一个 StopIteration 异常。

2、生成器(generator)【一种特殊迭代器】

1)是通过yield语句快速生成迭代器,可以不用iter和next方法 ;

2)yield可以使一个普通函数变成一个生成器,并且相应的next()方法返回是yield后的值。一种更直观的解释是:程序执行到yield时会返回结果并暂停,再次调用next时会从上次暂停的地方继续开始执行

3)生成器自身有构成一个迭代器,每次迭代时使用一个yield返回 的值,一个生成器中可以有多个yield的值

4)创建生成器的方法:

a、()

    

b、yield    

def create_num(all_num):
    # a = 1
    # b = 1
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a              # 如果一个函数中有yield语句,那么这个就不在是函数,而是一个生成器的模板
        a, b = b, a + b
        current_num += 1

if __name__ == ‘__main__‘:
    # 如果在调用create_num的时候,发现这个函数有yield,此时不是调用函数,而是创建一个生成器对象
    obj = create_num(10)
    for num in obj:
        print(num)

5、send使用--启动生成器

def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        res = yield a
        print(">>>>ret>>>>", res)
        a, b = b, a + b
        current_num += 1

if __name__ == ‘__main__‘:
    obj = create_num(4)
    # obj.send(None) # send一般不会放到第一次启动生成器,如果非要如此,传递None
    ret = next(obj)
    print(ret)
    ret = obj.send("hhhhh")
    print(ret)
    # send里面的数据,会传递给第5行,当作yield a的结果,然后res保存这个结果..
    # send的结果是下一次调用yield时,yield后面的值
    ret = obj.send(None)
    print(ret)
    ret = obj.send(None)
    print(ret)

6、yield和return区别

             yield可以暂停函数执行,且下一次执行时候恢复

3、迭代器和生成器作用

  • 迭代器: 减少内存空间, 能实现循环
  • 生成器: 能让一个函数看上去能暂停执行
  • 都是保证生成数据代码, 不是保存结果

4、多任务-协程(yield执行)

  a、生成器函数其实就是实现了协程,即使用yield, send(), next()实现协程

  b、进程占资源最多, 其次线程, 协程占资源最少!

  

#!/bin/python3
# -*- coding=utf-8 -*-

import time
def task_1():
    while True:
        print("------1-------")
        time.sleep(0.1)
        yield 

def task_2():
    while True:
        print("------2------")
        time.sleep(0.2)
        yield 

def main():
   t1 = task_1()
   t2 = task_2()
   while True:
       next(t1)
       next(t2)
if __name__ == "__main__":
    main()

  并行: 有两个任务, 但是有四个CPU的核, 一个任务占一个核, 每个都在做

  并发: 有很多任务, 但是只有两个核, 所以 交替执行

  4.1、greenlet实现多任务(核心还是yield)

           

#!/bin/python3
# -*- encoding=utf-8 -*-

from greenlet import greenlet
import time
def 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()

  4.2、gevent实现协程(更强大,常用)

    

#!/bin/python3
# -*-encoding=utf-8-*-

import gevent
import time
def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)
def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)
def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        gevent.sleep(0.5)
print("----1-----")
g1 = gevent.spawn(f1, 5)
print("----2-----")
g2 = gevent.spawn(f2, 5)
print("----3-----")
g3 = gevent.spawn(f3, 5)
print("----4-----")
g1.join()
g2.join()
g3.join()

gevent遇到延时操作就切换, 利用了等待耗时的操作, 去做其他的事情

如下: 加入monkey.patch_all()则无须将 time.sleep()改成 gevent.sleep()

#!/bin/python3
# -*-encoding=utf-8-*-

import gevent
from gevent import monkey
import time
monkey.patch_all()
def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)

def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
 #       gevent.sleep(0.5)

def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)

print("----1-----")
g1 = gevent.spawn(f1, 5)
print("----2-----")
g2 = gevent.spawn(f2, 5)
print("----3-----")
g3 = gevent.spawn(f3, 5)
print("----4-----")
g1.join()
g2.join()
g3.join()

如下: 将需要join的代码, 写成列表, 简洁

#!/bin/python3
# -*-encoding=utf-8-*-

import gevent
from gevent import monkey
import time
monkey.patch_all()
def f1(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)

def f2(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
 #       gevent.sleep(0.5)

def f3(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        time.sleep(0.5)
#        gevent.sleep(0.5)

gevent.joinall([
    gevent.spawn(f1, 5),
    gevent.spawn(f2, 5),
    gevent.spawn(f3, 5)
    ])  

  4.3并发下载器

#!/bin/python3
#-*- encoding=utf-8 -*-

import gevent
import urllib.request
from gevent import monkey
monkey.patch_all()
def downloader(img_name, img_url):
    req = urllib.request.urlopen(img_url)
    img_content = req.read()

    with open("./img/"+ img_name, "wb") as f:
        f.write(img_content)
def main():
    gevent.joinall([
            gevent.spawn(downloader, "1.jpg", ‘https://rpic.douyucdn.cn/asrpic/190417/5440020_3968619_65b10_2_2142.jpg‘),
            gevent.spawn(downloader, ‘2.png‘, "https://rpic.douyucdn.cn/asrpic/190417/594613_2143.png")
        ])
if __name__=="__main__":
    main()

5、进程/线程/协程对比

  • 进程: 耗费资源最多, 进程里一定有一个线程, 默认线程称为主线程。进程是资源分配的单位。(最稳定, 耗费资源最多)
  • 线程线程是操作系统调度的单位. 线程切换需要的资源一般, 效率一般 (不考虑GIL情况) 
  • 协程: 协程切换任务资源很小, 效率高;
    • 特点: 在等待某个资源到来 期间, 去执行其他代码....多线程里有很多网络堵塞, 推荐先用协程 !
  • 多进程、多线程根据cpu核数不一样 可能是并行的, 但是协程是在一个线程中, 所以是并发的!

原文地址:https://www.cnblogs.com/liugp/p/11074917.html

时间: 2024-11-07 23:05:31

迭代器--》生成器--》协程的关系与区别的相关文章

10.迭代器/生成器/协程函数/列表生成器

迭代器为什么要用迭代器?小结:生成器为什么要使用生成器,什么是生成器?如何创建一个生成器举个栗子:斐波拉契数列用yield返回结果的执行流程作业代码以及注释:协程函数面向过程编程作业以及代码注解:典型范例以及代码解析:列表生成式生成器表达式作业和练习 迭代器 为什么要用迭代器? 提供了一种不依赖索引的取值方式,使一些不具有索引属性的对象也能遍历输出 相比列表,迭代器的惰性计算更加节约内存. 但是它无法有针对性地指定取某个值,并且只能向后取值. >>> from collections i

状态机/迭代器/LINQ/协程

状态机 有限状态机(Finite State Machine 或 Finite State Automata)是软件领域中一种重要的工具. 状态机允许一个对象在其内部状态改变时改变它的行为.对象内部状态决定行为方式,对象状态改变行为方式改变,这里强调内部状态. Command 模式是将命令请求封装成一个为对象,将不同的请求对象参数化以达到同样的调用执行不同的命令: State 模式是将对象的状态封装成一个对象,是在不同的状态下同样的调用执行不同的操作. 迭代器是一个典型的状态机例子,后续会讲解.

007-迭代器-生成器-协程

迭代器 : 区分 : 可迭代 ---> 实现了__iter__() 的类 可迭代对象 ---> 实现了 __iter__() 的类的实例 迭代器(对象) ---> 实现了 __iter__() 和 __next__() 的类的实例 作用 : 一个可以记住遍历的位置的对象,真正能够获取位置内容的是 next() 如何自定义一个迭代器对象? 要创建两个类,一个普通类,一个迭代器类,合起来就是迭代器对象 定义一个普通类,在普通类中实现 __iter__(),--> 此时,普通类的对象就是

【Python】【控制流程】【生成器 | 协程 | 期物 | 任务】对比与联系

Python 的 asyncio 类似于 C++ 的 Boost.Asio. 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知. Asyncio 是并发(concurrency)的一种方式.对 Python 来说,并发还可以通过线程(threading)和多进程(multiprocessing)来实现. Asyncio 并不能带来真正的并行(parallelism).当然,因为 GIL(全局解释器锁)的存在,Python 的多线程也不

浅谈进程、线程和协程三者之间的区别和联系

一.进程.线程.协程 1,进程 经典定义:一个执行中程序的实例.系统中的每个程序都运行在某个进程的上下文中.(-摘自 CSAPP) 进程是系统资源分配的最小单位 2,线程(thread) 线程就是运行在进程上下文中的逻辑流. 线程是操作系统能够进行运算调度的最小单位. 3,协程 相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛. 根据维基百科对子例程的描述:是一个大型程序中的某部分代码,由一个或多个语句块组成.它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性.我可

进程、线程、协程的关系

参考来源:https://blog.csdn.net/chengqiuming/article/details/80573288    https://www.cnblogs.com/guolei2570/p/8810536.html 协程不是进程,也不是线程,它就是一个函数,一个特殊的函数——可以在某个地方挂起,并且可以重新在挂起处继续运行.所以说,协程与进程.线程相比,不是一个维度的概念.一个进程可以包含多个线程,一个线程也可以包含多个协程,也就是说,一个线程内可以有多个那样的特殊函数在运行

协程与多线程的区别

转载: ----------------------------------------------------------------------第一篇------------------------------------------------------------------- coroutine和真正的thread的区别挺多,但是也不好说是限制,须知Contiki的使用环境是传感器网络节点,一般来说,大都是内存极其受限,相比之下,CPU资源是可以不那么受限的(或者这么说,CPU工作时

进程和线程、协程的区别

现在多进程多线程已经是老生常谈了,协程也在最近几年流行起来.python中也有协程库,tornado中也用了gevent封装好的协程.本文主要介绍进程.线程和协程三者之间的区别. 一.概念 1.进程 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信.由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈.寄存器.虚拟内存.文件句柄等)比较大,但相对比较稳定安全. 2.线

并发编程--协程

协程,又称为微线程,可以理解成可切换的函数,或生成器,协程中始终在单线程中执行,因此没有资源冲突问题,不需要锁机制.以下以菲波那切数列为例,加上自己的一些理解,稍微聊一下这个东西. 斐波那契数列的普通实现 一般的函数只能有一个返回值,return,且return后程序不再执行.如下: # 斐波那契数列的普通实现 def fib(n): res = [0] * n index = 0 a = 0 b = 1 while index < n: res[index] = b a, b = b, a+b