前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python协程回顾

python协程回顾

作者头像
小闫同学啊
发布2019-07-18 10:54:24
4150
发布2019-07-18 10:54:24
举报
文章被收录于专栏:小闫笔记小闫笔记

1. 迭代器

1.1 迭代

通过迭代器不断取出可迭代对象中的下一个元素的值 的过程

for

1.2 可迭代对象

能能被迭代的对象

from collections import Utterable

isinstance(对象, Iterable)

实现 __iter__提供迭代器

代码语言:javascript
复制
from collections import Iterable

data = [1,2,3,4,5]
for i in data:
    print(i)

# 1 通过一个记录位置的对象  不断地间接访问 对象中数据的过程  - 迭代
# 2 可迭代对象 - 可以被迭代的对象  for循环
# 3 如何判断一个对象是否是可迭代类型的对象  isinstance(对象, 类型)
#   常见的容器类型 就是典型的可迭代类型
print("判断data是否是列表类型", isinstance(data, list))
# 4 自定义可迭代类型 实现类中 __iter__方法 的类就是可迭代类型
class MyRange(object):
    def __init__(self, n):
        self.n = n
    def __iter__(self):
        """实现一个可迭代类型  一定要实现本方法 返回一个迭代器"""
        pass

print("判断MyRange是否是可迭代类型", isinstance(MyRange(5), Iterable))


for i in MyRange(5):
    print(i)



print("判断data是否是可迭代类型", isinstance(data, Iterable))
print("判断整数是否是可迭代类型", isinstance(123, Iterable))
print("判断元组是否是可迭代类型", isinstance((1,2,3), Iterable))
print("判断字符串是否是可迭代类型", isinstance("123456", Iterable))
print("判断字符串是否是可迭代类型", isinstance({1:2}, Iterable))

1.3 迭代器

迭代的访问 可迭代对象中的下一个元素

应用场景: 用户只需要关心如何使用迭代器访问数据 而不需要关键数据该如何访问

解耦合

每一种可迭代对象都会提供对应的迭代器

from collections import Iterator

isinstance(对象,Iterator)

实现

__next__提供下一个元素的值

__iter__返回自身即可 应对情况 迭代器 = iter(迭代器)

代码语言:javascript
复制
from collections import Iterator,Iterable

# 1 迭代器Iterator- 记录每次访问的位置信息的对象  便于下一次访问 (当访问完成后会自动执行下一个位置)
# 2 应用场景 不同可迭代对象有不同的访问方式  每一种可迭代对象都会提供一个迭代器
#           用户只需要通过迭代器取出 下一个元素 的值即可 而不用具体访问细节 - 解耦合

# 3 自定义迭代器类型 实现__next__方法
class MyRangeIter(object):
    """迭代器类"""
    def __init__(self, n):
        self.n = n
        self.i = 0
    def __next__(self):
        """实现迭代器类型一定要实现该方法  目的:返回用户需要的下一个元素的值"""
        if self.i < self.n:
            self.i += 1
            return self.i
        else:
            # 停止 迭代    当迭代器迭代完成的时候 抛出这个异常
            raise StopIteration
    def __iter__(self):
        """Python规定迭代器 必须是可迭代对象 因此也必须使用该方法"""
        return self
class MyRange(object):
    """可迭代类型"""
    def __init__(self, n):
        self.n = n
    def __iter__(self):
        """实现一个可迭代类型  一定要实现本方法 目的: 返回一个迭代器"""
        return MyRangeIter(self.n)
# 4 判断迭代器类型
print("判断迭代器类型",isinstance(MyRangeIter(5), Iterator))
print("判断迭代器是否是可迭代对象",isinstance(MyRangeIter(5), Iterable))
print("判断可迭代对象是否是迭代器",isinstance(MyRange(5), Iterator))
for i in MyRange(5):
    print(i)
    input(":")

1.4 next函数 iter函数

iter函数 迭代器 = iter(可迭代对象)

取出可迭代对象中提供的迭代器

next函数 下一个元素的值 = next(迭代器)

通过迭代器取出下一个元素的值

2. 生成器

是一种特殊的迭代器, 支持迭代器所有的操作

2.1. 生成器表达式

列表推导式[] ——> ()

优点: 节约资源, 不用占用大量空间

在用户需要访问数据的时候 才延迟产生

2.2 生成器函数

含有yield关键字的函数 不再是普通函数 而是生成器函数

代码语言:javascript
复制
def myrange(n):
    start = 0
    while start < n:
        # 1 执行到yield会暂停当前代码执行 将后面的值返回到调用生成器代码的地方
        # 2 当前生成器代码再次执行时 直接从上次暂停的地方继续往下执行
        yield start
        return 1000  # 正常情况下 当前生成器会执行3次才会停止迭代 而有return关键字就在第二次停止迭代了
        start += 1

# 生成器函数不是普通函数  调用 生成器函数() 产生生成器对象
# 执行生成器函数的代码    需要调用next(生成器对象)
# yield关键字的作用
gen = myrange(3)
i = next(gen)
print(i)
try:
    i = next(gen)
    print(i)
except StopIteration as e:
    # 如果需要获取到 生成器最终return的值 需要捕获异常
    print("接收到了异常数据 %s" % e)
    pass

# i = next(gen)
# print(i)
# print(next(gen))  # 迭代完成 StopIteration
#

执行生成器并且传参:

代码语言:javascript
复制
def myrange(n):
    start = 0
    while start < n:
        # 1 执行到yield会暂停当前代码执行 将后面的值返回到调用生成器代码的地方
        # 2 当前生成器代码再次执行时 直接从上次暂停的地方继续往下执行
        number = yield start
        print("接收到了数据%s" % number)
        start += 1

if __name__ == '__main__':
    gen = myrange(3)
    # 生成器支持操作-next函数  迭代器支持的操作
    i = next(gen)
    print(i)
    # 生成器支持操作-send方法  特有
    i = next(gen)
    print(i)
    i = gen.send(100)
    print(i)
    # 对比 两种执行生成器的方式的异同
    # 1 同: 都可以执行生成器代码 并且获取到一个值
    # 2 异: next不可以发送数据; send可以发送数据;
    #   第一次调用时send(None), next没有这个要求

2.3 yield关键字作用

代码语言:javascript
复制
1 执行到yield会暂停当前代码执行 将后面的值返回到调用生成器代码的地方
2 当前生成器代码再次执行时 直接从上次暂停的地方继续往下执行

3. 协程

3.1 概念

协程是 用户层面的多任务调度机制 数量可以几十万

进程线程是操作系统层面的多任务机制 数量受操作系统的限制

了解greenlet / yield使用协程

掌握gevent模块的使用

使用yield实现协程:

代码语言:javascript
复制
import time


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

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

if __name__ == '__main__':
    # 创建生成器对象
    w1 = work1()
    w2 = work2()
    while True:
        next(w1)
        next(w2)

使用greenlet实现多任务切换:

代码语言:javascript
复制
import time
import greenlet   # py3环境


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

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

if __name__ == '__main__':
    # 创建协程
    g1 = greenlet.greenlet(work1)
    g2 = greenlet.greenlet(work2)
    # 切换到第一个协程执行   手动切换
    g1.switch()

3.2 gevent模式

创建启动协程 协程对象 = gevent.spawn(入口, 参数列表)

等待协程完成 协程对象.join()

等待多个协程完成 gevent.joinall()

自动切换 from gevent import monkey; monkey.patch_all()

代码语言:javascript
复制
from gevent import monkey
monkey.patch_all()   # 破解代码 实现自动切换 默认不能自动切换
import gevent
import time

# 能够切换的操作 time.sleep(1) recv  accept  将默认阻塞的操作变为非阻塞的操作

def worker(no):
    """协程函数"""
    for i in range(3):
        print("这是协程 %s %s" % (no, gevent.getcurrent()))
        time.sleep(1)  # gevent.sleep(1)

def main():
    # 创建协程 自动运行
    g1 = gevent.spawn(worker, 1111)
    g2 = gevent.spawn(worker, 2222)

    # 阻塞等待协程执行完成  - 主要目的是需要让主进程主线程阻塞在这里
    # g1.join()
    # g2.join()
    gevent.joinall([g1, g2])

if __name__ == '__main__':
    main()

3.3 多任务的选择

资源消耗不关心 要求稳定 用户多进程

资源消耗关心 多线程或者协程

多任务的网络程序 建议优先使用协程

多任务图片下载器

代码语言:javascript
复制
from gevent import monkey
monkey.patch_all()   # 启动切换
import gevent
import urllib.request
import time
def down_img(url):
    """下载指定路径的图片 参数就是图片的链接地址-网址 URL URI"""
    # 1 从URL中解析出文件名称
    # https://rpic.douyucdn.cn/live-cover/roomCover/2018/08/15/a0c1206ae5da0c02a677506af9f41c40_big.jpg
    file_name = url[url.rfind("/") + 1: ]
    print("开始下载图片 %s" % file_name)

    # 2 响应对象<网页数据> 请求    打开网址获取资源对象
    response = urllib.request.urlopen(url)

    # 3 解析出其中数据 bytes
    data = response.read()

    # 4 写入文件
    with open(file_name, "wb") as file:
        file.write(data)

    print("完成下载图片 %s" % file_name)
def main():
    begin = time.time()
    # 1 创建协程
    img_list = [
        "https://rpic.douyucdn.cn/live-cover/roomCover/2018/08/15/a0c1206ae5da0c02a677506af9f41c40_big.jpg",
        "https://rpic.douyucdn.cn/live-cover/roomCover/2018/10/10/2694864718ef0772c36712cd0e12e3a7_big.jpg",
        "https://rpic.douyucdn.cn/live-cover/roomCover/2018/10/29/b28ff3c2352b21c766b823db35efc767_big.jpg"]
    g1 = gevent.spawn(down_img, img_list[0])
    g2 = gevent.spawn(down_img, img_list[1])
    g3 = gevent.spawn(down_img, img_list[2])
    # 2 等待协程执行完成
    gevent.joinall([g1, g2, g3])
    end = time.time()
    print("花费了%f秒" % (end-begin))
if __name__ == '__main__':
    main()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈技术精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 迭代器
    • 1.1 迭代
      • 1.2 可迭代对象
        • 1.3 迭代器
          • 1.4 next函数 iter函数
          • 2. 生成器
            • 2.1. 生成器表达式
              • 2.2 生成器函数
                • 2.3 yield关键字作用
                • 3. 协程
                  • 3.1 概念
                    • 3.2 gevent模式
                      • 3.3 多任务的选择
                        • 多任务图片下载器
                        相关产品与服务
                        容器服务
                        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档