当前位置: 首页 > news >正文

python--local对象、flask上下文源码分析

一、local对象

背景:
多线成并发操作一个变量,会导致数据错乱,可以使用互斥锁加锁处理数据不安全的情况 (临界区)

解决:
使用local对象处理,多个线程操作的变量是local对象,就不会有并发安全的问题。因为它处理了并发安全的问题---->请求统一放在一个大字典中,key值是线程id号,value是个字典。

# {111:{'name':jack},222:{'name':roma}}
l=local()
l.name='jack'---->l[111][name]
l.name='roma'---->l[222][name]

1.基本使用
不使用local,多线程并发操作,数据错乱

import time
from threading import Thread
class Local():passl = Local()def task(name):l.name = name time.sleep(1)print('在线程内的名字是:', name, 'l对象中的名字大概率不一样', l.name)if __name__ == '__main__':for i in range(10):t = Thread(target=task, args=['jack' + str(i) + '号', ])t.start() # 等待所有线程都执行完成再执行下面代码time.sleep(6)print(l)

使用local

import time
from threading import Thread
from threading import local# 定义一个全局变量,并发安全的local,多个线程操作,不会错乱,因为每个线程用的都是自己的数据
l = local()def task(name):l.name = nametime.sleep(1)print('在线程内的名字是:', name, 'l对象中的名字也是', l.name)if __name__ == '__main__':for i in range(10):t = Thread(target=task, args=['jack' + str(i) + '号', ])t.start()# 等待所有线程都执行完成再执行下面代码time.sleep(6)print(l)

2.自己写一个local类,线程和协程并发安全

通过字典自定义threading.local(函数)

from threading import get_ident,Thread
import time
storage = {}
def set(k,v):ident = get_ident()  # 线程id号if ident in storage:storage[ident][k] = velse:storage[ident] = {k:v}
def get(k):ident = get_ident()return storage[ident][k]
def task(arg):set('val',arg) #v = get('val')print(v)# 10个线程跑完,最终storage={123:{val:0},222:{val:1},333:{val:2},444:{val:3}.....}
for i in range(10):t = Thread(target=task,args=(i,))t.start()

使用面向对象

from threading import get_ident,Thread
import time
class Local(object):storage = {}def set(self, k, v):ident = get_ident()if ident in Local.storage:Local.storage[ident][k] = velse:Local.storage[ident] = {k: v}def get(self, k):ident = get_ident()return Local.storage[ident][k]
obj = Local()
def task(arg):obj.set('val',arg)v = obj.get('val')print(v)
for i in range(10):t = Thread(target=task,args=(i,))t.start()

通过__setattr__和__getattr__方法实现

from threading import get_ident,Thread
import time
class Local(object):storage = {}def __setattr__(self, k, v):ident = get_ident()if ident in Local.storage:Local.storage[ident][k] = velse:Local.storage[ident] = {k: v}def __getattr__(self, k):ident = get_ident()return Local.storage[ident][k]
obj = Local()
def task(arg):obj.val = argprint(obj.val)
for i in range(10):t = Thread(target=task,args=(i,))t.start()

每个local对象用自己的存储空间(字典)

from threading import get_ident, Thread
import timeclass Local(object):def __init__(self):# self.storage={}  # 不能这样写 会递归object.__setattr__(self, 'storage', {})def __setattr__(self, k, v):ident = get_ident()  # 获取线程id号if ident in self.storage:self.storage[ident][k] = velse:self.storage[ident] = {k: v}    #def __getattr__(self, k):ident = get_ident()return self.storage[ident][k]obj = Local()def task(name):obj.name = nameprint(obj.name)for i in range(10):t = Thread(target=task, args=(i,))t.start()

兼容线程和协程

try:from greenlet import getcurrent as get_ident
except Exception as e:from threading import get_identfrom threading import Thread
import time
class Local(object):def __init__(self):object.__setattr__(self,'storage',{})def __setattr__(self, k, v):ident = get_ident()if ident in self.storage:self.storage[ident][k] = velse:self.storage[ident] = {k: v}def __getattr__(self, k):ident = get_ident()return self.storage[ident][k]
obj = Local()
def task(arg):obj.val = argobj.xxx = argprint(obj.val)
for i in range(10):t = Thread(target=task,args=(i,))t.start()

二、flask上下文源码分析

请求上下文执行流程(ctx):

-0 flask项目一启动,有6个全局变量-_request_ctx_stack:LocalStack对象---->封装了local-_app_ctx_stack :LocalStack对象-request : LocalProxy对象-session : LocalProxy对象-1 请求来了 app.__call__()---->内部执行:self.wsgi_app(environ, start_response)-2 wsgi_app()-2.1 执行:ctx = self.request_context(environ):返回一个RequestContext对象,并且封装了request(当次请求的request对象),session-2.2 执行: ctx.push():RequestContext对象的push方法-2.2.1 push方法中中间位置有:_request_ctx_stack.push(self),self是ctx对象-2.2.2 去_request_ctx_stack对象的类中找push方法(LocalStack中找push方法)-2.2.3 push方法源码:def push(self, obj):#通过反射找self._local,在init实例化的时候生成的:self._local = Local()#Local()flask封装的支持线程和协程的local对象# 一开始取不到stack,返回Nonerv = getattr(self._local, "stack", None)if rv is None:#走到这,self._local.stack=[],rv=self._local.stackself._local.stack = rv = []# 把ctx放到了列表中#self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}rv.append(obj)return rv-3 如果在视图函数中使用request对象,比如:print(request)-3.1 会调用request对象的__str__方法,request类是:LocalProxy-3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())-3.2.1 内部执行self._get_current_object()-3.2.2 _get_current_object()方法的源码如下:def _get_current_object(self):if not hasattr(self.__local, "__release_local__"):#self.__local()  在init的时候,实例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)# 用了隐藏属性#self.__local 实例化该类的时候传入的local(偏函数的内存地址:partial(_lookup_req_object, "request"))#加括号返回,就会执行偏函数,也就是执行_lookup_req_object,不需要传参数了#这个地方的返回值就是request对象(当此请求的request,没有乱)return self.__local()try:return getattr(self.__local, self.__name__)except AttributeError:raise RuntimeError("no object bound to %s" % self.__name__)-3.2.3 _lookup_req_object函数源码如下:def _lookup_req_object(name):#name是'request'字符串#top方法是把第二步中放入的ctx取出来,因为都在一个线程内,当前取到的就是当次请求的ctx对象top = _request_ctx_stack.topif top is None:raise RuntimeError(_request_ctx_err_msg)#通过反射,去ctx中把request对象返回return getattr(top, name)-3.2.4 所以:print(request) 实质上是在打印当此请求的request对象的__str__-4 如果在视图函数中使用request对象,比如:print(request.method):实质上是取到当次请求的reuquest对象的method属性-5 最终,请求结束执行: ctx.auto_pop(error),把ctx移除掉

其他的东西:

-session:-请求来了opensession-ctx.push()---->也就是RequestContext类的push方法的最后的地方:if self.session is None:#self是ctx,ctx中有个app就是flask对象,   self.app.session_interface也就是它:SecureCookieSessionInterface()session_interface = self.app.session_interfaceself.session = session_interface.open_session(self.app, self.request)if self.session is None:#经过上面还是None的话,生成了个空sessionself.session = session_interface.make_null_session(self.app)-请求走了savesession-response = self.full_dispatch_request() 方法内部:执行了before_first_request,before_request,视图函数,after_request,savesession-self.full_dispatch_request()---->执行:self.finalize_request(rv)-----》self.process_response(response)----》最后:self.session_interface.save_session(self, ctx.session, response)-请求扩展相关before_first_request,before_request,after_request依次执行-信号的触发信号名.send()-flask有一个请求上下文,一个应用上下文-ctx:-是:RequestContext对象:封装了request和session-调用了:_request_ctx_stack.push(self)就是把:ctx放到了那个位置-app_ctx:-是:AppContext(self) 对象:封装了当前的app和g-调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置-g是个什么鬼?专门用来存储用户信息的g对象,g的全称的为global g对象在一次请求中的所有的代码的地方,都是可以使用的(当次请求中传递一些数据)-代理模式-request和session就是代理对象,用的就是代理模式-g对象和session的区别g对象只对当次请求有效(当此请求内有效)session:可以跨请求,该用户的多次请求中都可以使用

总结:
1 flask中间件,使用请求扩展,完成django中间件的功能

2 快速生成当前项目的依赖(两种方案)

  • pipreqs

3 函数和方法的区别

  • 面向对象中有方法的概念
  • 绑定给对象的,绑定给类的方法
  • 特殊之处是自动传值
  • 方法有可能是函数(对象的绑定方法,如果类来调用,就是函数)

4 偏函数 partial

  • 提前给函数传值

5 local对象

  • 解决并发安全的问题
  • 多条线程操作同一个变量,会出现数据安全问题,解决该问题,需要加锁
  • 每条线程操作的都是自己线程的数据
  • threading包下的local类---->实例化得到对象---->多线程并发操作---->数据不会错乱

6 自定义local对象

self.name='lqz'  # 会触发__setattr__---->会出现递归
setattr(self,'name','lqz')  # 会出现递归
object.__setattr__(self,'name','lqz')  # 不会出现递归
self.__dict__()  # 属性字典,也不会出现递归

7 flask请求上下文:RequestContext---->ctx:request,session,flash

8 flask应用上下文:AppContext---->app_ctx:当前app,g

9 ctx对象:请求上下文,flask整个请求的流程

请求来了---->app()---->触发Flask类的__call__方法---->app.wsgi_app()ctx = self.request_context(environ) # ctx中包含当此请求的request,session,把ctx放到了local对象中,来一个请求就放一次,local处理了并发安全,所以自己放的都是自己的,相互不影响response = self.full_dispatch_request()# 执行请求扩展,执行视图函数或者视图类,处理了session,还有信号不管在整个过程中是否出异常,ctx都从local对象上移除

10 flask请求上下文源码分析

1 请求来了执行---->app()---->触发类的__call__---->self.wsgi_app(environ, start_response)--->2 ctx = self.request_context(environ)---->封装了request和session3 ctx.push()---->_request_ctx_stack.push(self)---->self是ctx4 _request_ctx_stack是LocalStack类的对象---->push
5 LocalStack的push方法源码def push(self, obj):rv = getattr(self._local, "stack", None)if rv is None:self._local.stack = rv = []# self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}rv.append(obj)return rv6 LocalStack对像中的  _local--->init初始化出来的
self._local = Local() # 咱们自己写的可以多线程并发访问的Local7 local={线程或协程id:{stack:[ctx]},线程或协程id:{stack:[ctx]}}
local.stack---->取stack的值,在不同协程下,取到的是自己的8 在视图函数中使用request,session---->都是当此请求的request和session,但是我们使用了全局变量。打印的真的是当次请求的Request类的对象,但实际上request根本不是Request类的对象,LocalProxy类的对象,LocalProxy类重写了__str__9 print(request)的时候---->类的__str__10 LocalProxy把所有的魔法方法都重写了,因为它是个代理类11 request = LocalProxy(partial(_lookup_req_object, "request"))
init---->def __init__(self, local, name=None):---->object.__setattr__(self, "_LocalProxy__local", local)--->local就是偏函数12 LocalProxy---》__str__--->_get_current_object()是LocalProxy类的方法13 LocalProxy._get_current_object()---return self.__local()--》加括号执行偏函数---》partial(_lookup_req_object, "request")()--->_lookup_req_object('request')14 返回了当前线程所在的ctx中的request对象15 request.method-->就是当前线程的request对象的method方法16 在视图函数中打印print(session)--->是当此请求的session# 应用上下文
# g到底是什么,是一个全局变量,放和取在当次请求中的数据# session:open_session   save_session# 信号的触发位置
# 请求扩展中三个:的执行位置

相关文章:

python--local对象、flask上下文源码分析

一、local对象 背景: 多线成并发操作一个变量,会导致数据错乱,可以使用互斥锁加锁处理数据不安全的情况 (临界区) 解决: 使用local对象处理,多个线程操作的变量是local对象,就不会…...

类文件一些内容

1、类加载 将类的字节码加载到JVM中,并转换为可以被JVM运行的数据结构的过程 类文件结构...

28 Java练习——实现两个集合的交集和并集

求并集的思路:假设传入的是一个ArrayList对象,求并集的时候直接调用其中一个List集合的addAll方法将另一个集合合并过来,而List的特性是有序,重复的。因此,使用Set接口的无序不可重复的特性,把Collection对…...

ES6学习-Promise

Promise 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 语法上: Promise 是一个对象,从它可以获取异步操作的消息。 特点 对象的状态不受外界影响。Promise 对象戴白哦一个异步操…...

《Web安全基础》03. SQL 注入

web 1:简要 SQL 注入2:MySQL 注入2.1:信息获取2.2:跨库攻击2.3:文件读写2.4:常见防护 3:注入方法3.1:类型方法明确3.2:盲注3.3:编码3.4:二次注入3…...

算法与数据结构(二十一)二叉树(纲领篇)

备注:本文旨在通过 labuladong 的二叉树(纲领篇)理解框架思维,用于个人笔记及交流学习,版权归原作者 labuladong 所有; 我刷了这么多年题,浓缩出二叉树算法的一个总纲放在这里,也许…...

visio,word添加缺少字体,仿宋_GB2312、楷体_GB2312、方正小标宋简体等字体下载

一. 内容简介 visio,word添加缺少字体,仿宋_GB2312、楷体_GB2312、方正小标宋简体等字体下载 二. 软件环境 2.1 visio 三.主要流程 3.1 下载字体 http://www.downza.cn/ 微软官方给的链接好多字体没有,其他好多字体网站,就是给你看个样式&#xff…...

Java爬虫

什么是爬虫? 通过请求,从而去获取互联网上的各种数据与资源,如文字,图片,视频。 本质上原理都一样,都是通过api请求,然后服务器就会发给你信息,然后你再根据这些信息去提取你想要的…...

海外应用商店优化实用指南之关键词

和SEO一样,关键词是ASO中的一个重要因素。就像应用程序标题一样,在Apple App Store和Google Play中处理应用程序关键字的方式也有所不同。 关键词研究。 对于Apple,我们的所有关键词只能获得100个字符,Google Play没有特定的关键…...

element+vue 之动态form

1.页面部分 <div v-for"(item,index) in formList" :key"index"><el-col :span"6" v-if"item.inputType0"><el-form-item :label"item.conditionName" :prop"item.conditionCode":rules"{req…...

winform学习(3)-----Windows窗体应用和Windows窗体应用(.Net Framework)有啥区别?

1.模板选择 在学习winform的时候总是会对这两个应用不知道选择哪个&#xff1f;而且在学习的时候也没有具体的说明 首先说一下我是在添加控件的时候出现了以下问题 对于使用了Windows窗体应用这个模板的文件在工具箱中死活不见控件。 在转换使用了Windows窗体应用(.NET Fram…...

虚拟化中的中断机制:X86与PIC 8259A探索(上)

本系列深入探讨虚拟化中断技术&#xff0c;从X86架构和PIC 8259A的基础&#xff0c;到IOAPIC和MSI的编程&#xff0c;再到MSIX技术与Broiler设备的实战应用&#xff0c;全面剖析中断虚拟化的前沿进展。 X86 中断机制 ​ 在计算机架构中&#xff0c;CPU 运行的速度远远大于外设…...

软件外包开发语言排行榜

软件开发语言的排行榜是一个动态的话题&#xff0c;而在未来的几年中&#xff0c;新的技术和语言可能会不断涌现&#xff0c;影响排名。然而以下是一些在过去几年中一直受欢迎并有前途的软件开发语言&#xff0c;如果是新入门软件开发行业在学习语言做选择&#xff0c;希望下面…...

BI技巧丨利用OFFSET计算同环比

微软最近更新了很多开窗函数&#xff0c;其内部参数对比以往的DAX函数来说&#xff0c;多了很多&#xff0c;这就导致学习的时间成本直线上升。 而且对于新增函数的应用场景&#xff0c;很多小伙伴也是一知半解的&#xff0c;本期我们就来聊一聊关于最近新增的开窗函数——OFF…...

整理mongodb文档:collation

文章连接 整理mongodb文档:collation 看前提示 对于mongodb的collation。个人主要用的范围是在createcollection&#xff0c;以及find的时候用&#xff0c;所以本片介绍的时候也是这两个地方入手&#xff0c;对新手个人觉得理解概念就好。不要求强制性掌握&#xff0c;但是要…...

【LangChain】Prompts之Prompt templates

Prompts 编程模型的新方法是通过提示(prompts)。 prompts是指模型的输入。该输入通常由多个组件构成。 LangChain 提供了多个类和函数&#xff0c;使构建和使用prompts变得容易。 Prompt templates(提示模板): 参数化模型输入Example selectors(选择器示例): 动态选择要包含在…...

【数字IC基础】时序违例的修复

时序违例的修复 建立时间违例保持时间违例Buffer 插入位置参考资料 建立时间违例 基本思路是减少数据线的延时、减少 Launch clock line 的延时、增加capture clock line的delay 加强约束&#xff0c;重新进行综合&#xff0c;对违规的路径进行进一步的优化&#xff0c;但是一…...

深度学习实战46-基于CNN的遥感卫星地图智能分类,模型训练与预测

大家好,我是微学AI,今天给大家介绍一下深度学习实战46-基于CNN的遥感卫星地图智能分类,模型训练与预测。随着遥感技术和卫星图像获取能力的快速发展,卫星图像分类任务成为了计算机视觉研究中一个重要的挑战。为了促进这一领域的研究进展,EuroSAT数据集应运而生。本文将详细…...

Node.js-fs模块文件创建、删除、重命名、文件内容的写入、读取以及文件夹的相关操作

一、写入文件操作 异步写入&#xff1a;writeFile() 同步写入&#xff1a;writeFileSync() const fs require("fs"); fs.writeFile("目标文件路径", "要写入的内容", err > {if(err){console.log(err);return;}console.log("写入成功&a…...

LIN协议总结

目录 一、LIN是什么1、LIN的概念2、扩展介绍一下同步通信和异步通信的区别3、LIN连接结构及节点构成 二、LIN的特点三、LIN协议层1、帧的结构2、帧的类型3、进度表4、状态机实现5、网络管理6、状态管理 四、帧收发的硬件实现1、组成2、硬件特点3、协议控制器4、总线收发器5、LI…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...