【Python模块】——contextvars

Python模块之contextvars

  • 0. 前言
  • 1. 源码
  • 2. contextvar模块详解
    • 2.1 contextvar.ContextVar
    • 2.2 contextvar.Token
    • 2.3 contextvar.Context
      • 代码示例:
      • 解释
    • 2.4 asyncio模块
      • 2.4.1 使用contextvars隔离异步任务的变量
      • 2.4.2 使用contextvars代替threading.local()
  • 3. 参考资料

0. 前言

在阅读flask上下文管理器相关的文章,发现flask在2.3.0使用contextvar代替了threading.local实现线程变量跟踪。contextvar,顾名思义,是用来记录上下文的变量。自python3.7.0引入contextvar模块后,既可以像threading.local为每个线程维护各自的变量,还支持异步任务的变量跟踪。

1. 源码

https://peps.python.org/pep-0567/#abstract
https://docs.python.org/zh-cn/3/library/contextvars.html

2. contextvar模块详解

2.1 contextvar.ContextVar

  1. 使用ContextVar创建一个上下文管理对象
    ctx = ContextVar('ctx', default=42) # 创建一个上下文变量,name=ctx,默认值=42
    
    try:
        print(ctx.get())
        # 通过ctx.set()设置ctx的值
        token = ctx.set(100)
        # 通过ctx.get()获取ctx的值
        print(ctx.get())
    finally:
        # 通过ctx.reset()重置ctx的值,恢复到设置token操作之前的值
        ctx.reset(token)
    
    print(ctx.get())
    
    # 运行结果:
    """
    42
    100
    42
    """
    

注意:ctx.get()如果此前没有给ContextVar赋值或者分配默认值,调用此方法会提示LookupError

2.2 contextvar.Token

contextvar.Token官方解释: contextvars.Token is an opaque object that should be used to restore the ContextVar to its previous value, or to remove it from the context if the variable was not set before. It can be created only by calling ContextVar.set().

  • contextvar.Token 可以用来保存ContextVar之前的值,如果之前没有设置过则会被移除。
  • Token变量维护了两个属性:varold_value
    • token.var用来指向创建token的ContextVar;
    • token.old_value用来保存之前的值,如果之前未分配过值,则填充token.MISSING

2.3 contextvar.Context

contextvar.Context可以用来创建一个空的上下文;如果想使用当前上下文的拷贝,可以通过ctx = contextvars.copy_context()实现。

代码示例:

from contextvars import ContextVar, copy_context

var = ContextVar('var')
var.set('spam')
# print(var.get())

def main():
    # 'var' was set to 'spam' before
    # calling 'copy_context()' and 'ctx.run(main)', so:
    print("before var.get() ==", var.get())
    print("before ctx[var] ==", ctx[var])

    var.set('ham')

    # Now, after setting 'var' to 'ham':
    assert var.get() == ctx[var] == 'ham'
    print("after var.get() ==", var.get())
    print("after ctx[var] ==", ctx[var])

ctx = copy_context()

# Any changes that the 'main' function makes to 'var'
# will be contained in 'ctx'.
ctx.run(main)

# The 'main()' function was run in the 'ctx' context,
# so changes to 'var' are contained in it:

print("not in main, var.get() ==", var.get())

# However, outside of 'ctx', 'var' is still set to 'spam':
# var.get() == 'spam'
print("not in main, ctx[var] ==", ctx[var])

"""
执行结果:
before var.get() == spam
before ctx[var] == spam
after var.get() == ham
after ctx[var] == ham
not in main, var.get() == spam
not in main, ctx[var] == ham
"""

解释

  • 通过ContextVar创建变量var,并赋值=spam;
  • 使用ctx=copy_context()拷贝当前上下文,并使用ctx.run(main)执行main函数
    • main函数中对var的任何操作,都会被记录到ctx中,可通过ctx[var]查看
  • 执行完后,var退回原先的值,而ctx仍然记录着main函数中的操作记录

2.4 asyncio模块

2.4.1 使用contextvars隔离异步任务的变量

from contextvars import ContextVar
import asyncio

ctx = ContextVar('ctx')

async def ctx_get():
    print(f'Request ID (Inner)=={ctx.get()}')

async def ctx_set(value):
    ctx.set(value)
    await ctx_get()  # 设置完后去获取ctx
    print(f'Request ID (Outer)=={ctx.get()}')

async def main():
    tasks = []
    for value in range(1, 5):
        tasks.append(asyncio.create_task(ctx_set(value)))
    await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(main())

"""
执行结果:
Request ID (Inner)==1
Request ID (Outer)==1
Request ID (Inner)==2
Request ID (Outer)==2
Request ID (Inner)==3
Request ID (Outer)==3
Request ID (Inner)==4
Request ID (Outer)==4
"""

2.4.2 使用contextvars代替threading.local()

threading.local()实例

import threading
import time


local = threading.local()
def task(num):
    local.num = num
    ident = threading.get_ident()
    time.sleep(0.1)
    print(f'线程{ident}的local.num为{local.num}')


for i in range(5):
    t = threading.Thread(target=task, args=(i,))
    t.start()

contextvars实例

import threading
import contextvars

ctx = contextvars.ContextVar("ctx")

def task_get():
    ident = threading.get_ident()
    print(f"线程{ident}")
    return ctx.get()


def task(num):
    ctx.set(num)
    time.sleep(0.1)
    ident = threading.get_ident()
    print("线程{}的ctx为{}".format(ident, task_get()))

for i in range(5):
    t = threading.Thread(target=task, args=(i,))
    t.start()

3. 参考资料

https://blog.csdn.net/sinat_40572875/article/details/126856381
https://www.sohu.com/a/442518261_120918998
https://zhuanlan.zhihu.com/p/367753785

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/887068.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《15分钟轻松学 Python》教程目录

为什么要写这个教程呢,主要是因为即使是AI技术突起的时代,想要用好AI做开发,那肯定离不开Python,就算最轻量级的智能体都有代码块要写,所以不一定要掌握完完整整的Python,只要掌握基础就能应对大部分场景。…

使用VBA快速生成Excel工作表非连续列图片快照

Excel中示例数据如下图所示。 现在需要拷贝A2:A15,D2:D15,J2:J15,L2:L15,R2:R15为图片,然后粘贴到A18单元格,如下图所示。 大家都知道VBA中Range对象有CopyPicture方法可以拷贝为图片,但是如果Range对象为非连续区域,那么将产生10…

深刻理解Redis集群(下):Redis 哨兵(Sentinel)模式

背景 现在对3个节点的sentinel进行配置。sentinel的配置文件在redis的安装目录中已经存在,只需要复制到指定的位置即可。 sentinel是独立进程,有对应的脚本来执行。 基于之前的redis 一主二从的架构,我们继续启动3个sentinel进程。 哨兵模式的…

MAC备忘录空白解决方案

打开icloud->备忘录 取消勾选同步此MAC后再次勾选,然后点击完成即可。

【SpringCloud】服务注册/服务发现-Eureka

服务注册/服务发现-Eureka 1. 背景1.1 问题描述1.2 解决思路1.3 什么是注册中⼼1.4 CAP理论1.5 常⻅的注册中⼼ 2. Eureka 介绍3. 搭建Eureka Server 1. 背景 1.1 问题描述 上个章节的例⼦中可以看到, 远程调⽤时, 我们的URL是写死的 String url "http://127.0.0.1:90…

Ubuntu24.04远程开机

近来在几台机器上鼓捣linux桌面,顺便研究一下远程唤醒主机。 本篇介绍Ubuntu系统的远程唤醒,Windows系统的唤醒可搜索相关资料。 依赖 有远程唤醒功能的路由器(当前一般都带这个功能)有线连接主机(无线连接有兴趣朋友…

推荐:五种限流(Rate Limiting)算法

推荐:五种限流(Rate Limiting)算法,发现一个不错的讲这个算法的UP,地址是:05~五种限流(Rate Limiting)算法_哔哩哔哩_bilibili https://www.bilibili.com/video/BV11k4SerE74/ 全部用动画展示,十分生动,比如漏桶算法&…

芝法酱学习笔记(0.5)——使用jenkins做自动打包

前言 上节讲了SpringBoot上的打包。但这些过程都是手动的,在实际的开发测试时,自动化的打包部署,可以大大提升团队开发的效率 一、去官网下载 1.1 官网安装命令 对于如何安装的问题,我向来推荐官网 wget -O /usr/share/keyri…

针对考研的C语言学习(定制化快速掌握重点4)

typedef的使用 简化变量类型 逻辑结构 集合结构:无关系 线性结构:一对一 树形结构:一对多 图形结构:多对多 存储结构 顺序存储和链式存储(考代码) 顺序存储优点:1.可以实现随机存取。2.…

C语言 | Leetcode C语言题解之题451题根据字符出现频率排序

题目: 题解: #define HASH_FIND_CHAR(head, findint, out) HASH_FIND(hh, head, findint, sizeof(char), out) #define HASH_ADD_CHAR(head, intfield, add) HASH_ADD(hh, head, intfield, sizeof(char), add)struct HashTable {char key;int val;UT_ha…

今日凌晨,ChatGPT重磅更新!—— 我心目中的终极AGI界面

今日凌晨,ChatGPT重磅更新!—— 我心目中的终极AGI界面 我心目中的终极 AGI 界面是一张空白画布(canvas)。 今日凌晨,OpenAI 发布 canvas,一个与 ChatGPT 合作写作和编程的新界面! canvas&…

C语言复习概要(二)

本文目录 C语言中的数组与函数详解1. 引言2. 数组2.1. 什么是数组?语法:示例: 2.2. 数组的初始化示例 1:在声明时初始化示例 2:部分初始化示例 3:运行时赋值 2.3. 数组的访问与修改示例: 2.4. 多…

Docker启动 Redis提示:Can‘t initialize Background Jobg

问题说明: 在使用docker启动redis失败,但是查看容器日志,除了提示 Fatal:Cant initialize Background Jobg,没有其他错误信息。经过长时间查找资料及试错,现记录下可能的产生原因及解决方案,以便以后参考。 产生原因&…

【从零开始实现stm32无刷电机FOC】【实践】【7.1/7 硬件设计】

目录 stm32电路磁编码器电路电机驱动电路电流采样电路电机选择本文示例硬件说明 为了承载和验证本文的FOC代码工程,本节设计了一个简易的三相无刷电机 硬件套件,主控采用非常常用的stm32f103c8t6单片机,电机编码器采用MT6701,电机…

mysql怎么修改一个字段中的所有部分数据

UPDATE videos SET VideoCode replace(VideoCode,flv,mp4); update 表名 set 字段名 replace(字段名,‘修改前’,‘修改后’);

【工欲善其事】巧用 Sublime Text 生成带格式的 HTML 片段

文章目录 【工欲善其事】巧用 Sublime Text 生成带格式的 HTML 片段1 问题由来2 操作流程步骤1:打开代码片段定制页步骤2:在新标签页输入定制 XML步骤3:保存定义内容步骤4:功能测试 3 拓展 【工欲善其事】巧用 Sublime Text 生成带…

Elasticsearch使用Easy-Es + RestHighLevelClient实现深度分页跳页

注意!!!博主只在测试环境试了一下,没有发到生产环境跑。因为代码还没写完客户说不用弄了( •̩̩̩̩_•̩̩̩̩ ) 也好,少个功能少点BUG 使用from size的时候发现存在max_result_window10000的限制&…

如何使用工具删除 iPhone 上的图片背景

在 iPhone 上删除背景图像变得简单易行。感谢最近 iOS 更新中引入的新功能。如今,iOS 用户现在可以毫不费力地删除背景,而无需复杂的应用程序。在这篇文章中,您将学习如何使用各种方法去除 iPhone 上的背景。这可确保您可以选择最适合您偏好的…

自动驾驶核心技术:感知融合、规划决策、控制执行

1、前言 简单来说,实现自动驾驶需要解决三个核心问题:“我在哪?我要去哪?我该如何去?”能完整解决这三个问题就是真正的自动驾驶。 目前,自动驾驶汽车关键技术主要包括环境感知、精准定位、决策与规划、控制与执行、高精地图与车联网V2X以…

Linux下的IO模型

阻塞与非阻塞IO(Input/Output) 阻塞与非阻塞IO(Input/Output)是计算机操作系统中两种不同的文件或网络通信方式。它们的主要区别在于程序在等待IO操作完成时的行为。 阻塞IO(Blocking IO) 在阻塞IO模式下…