本段源码可以学习的地方:

1. 考虑到效率问题,可以通过上下文的机制,在属性被访问的时候临时构建;

2. 可以重写一些魔术方法,比如 __new__ 方法,在调用 object.__new__(cls) 前后进行属性的一些小设置;

3. 在本库中使用的重写魔术方法,上下文这两种基础之上,我们可以想到函数装饰器,类装饰器,异常捕获,以及两种上下文的结构;

灵活运用这些手法,可以让我们在代码架构上更上一层,能够更加省时省力。

 from weakref import ref  # ref用在了构造大字典元素元组的第一个位置即 (ref(Thread), 线程字典)
from contextlib import contextmanager # 上下文管理,用来确保__dict__属性的存在
from threading import current_thread, RLock
__all__ = ["local"] class _localimpl: # local()._local__impl = _localimpl() # local()实例的属性_local__impl就是这个类的实例
"""一个管理线程字典的类"""
__slots__ = 'key', 'dicts', 'localargs', 'locallock', '__weakref__' # _local__impl有这么多属性 def __init__(self):
# 这个self.key是用在线程对象的字典中的key
# self.key使用的一个字符串,这样既能运行的快,
# 但是通过'_threading_local._localimpl.' + str(id(self)也能保证不会冲突别的属性 self.key = '_threading_local._localimpl.' + str(id(self))
#
self.dicts = {} # 大字典
# 格式是: { id(线程1):(ref(Thread), 线程1自身的字典), id(线程2):(ref(Thread), 线程2自身的字典), ... } def get_dict(self): # 从大字典中拿(ref(Thread), 线程字典), 然后取线程字典
thread = current_thread()
return self.dicts[id(thread)][1] def create_dict(self): # 为当前线程创建一个线程字典,就是(ref(Thread), 线程字典)[1],即元组的第二部分
localdict = {}
key = self.key # key使用'_threading_local._localimpl.' + str(id(self)
thread = current_thread() # 当前线程
idt = id(thread) # 当前线程的id
def local_deleted(_, key=key): # 这个函数不看 pass
# When the localimpl is deleted, remove the thread attribute.
thread = wrthread()
if thread is not None:
del thread.__dict__[key]
def thread_deleted(_, idt=idt): # 这个函数不看 pass
# When the thread is deleted, remove the local dict.
# Note that this is suboptimal if the thread object gets
# caught in a reference loop. We would like to be called
# as soon as the OS-level thread ends instead.
local = wrlocal()
if local is not None:
dct = local.dicts.pop(idt)
wrlocal = ref(self, local_deleted)
wrthread = ref(thread, thread_deleted) # 大字典中每一个线程对应的元素的第一个位置: (ref(Thread), 小字典)
thread.__dict__[key] = wrlocal
self.dicts[idt] = wrthread, localdict # 在大字典中构造: id(thread) : (ref(Thread), 小字典)
return localdict @contextmanager
def _patch(self):
impl = object.__getattribute__(self, '_local__impl') # 此时的self是local(), 拿local()._local__impl
try:
dct = impl.get_dict() # 然后从拿到的local()._local__impl调用线程字典管理类的local()._local__impl.get_dict()方法
# 从20行到22这个get_dict()方法的定义可以看出来,拿不到会报KeyError的 except KeyError: # 如果拿不到报 KeyError之后捕捉
dct = impl.create_dict() # 然后再通过线程字典管理类临时创建一个
args, kw = impl.localargs # 这个时候把拿到
self.__init__(*args, **kw)
with impl.locallock: # 通过上下文的方式上锁
object.__setattr__(self, '__dict__', dct) # 给local() 实例增加__dict__属性,这个属性指向大字典中value元组的第二个元素,即线程小字典
yield # 到目前为止,local()类的两个属性都构造完成 class local: # local类
__slots__ = '_local__impl', '__dict__' # local类有两个属性可以访问 def __new__(cls, *args, **kw):
if (args or kw) and (cls.__init__ is object.__init__): # pass不看
raise TypeError("Initialization arguments are not supported")
self = object.__new__(cls) # pass不看
impl = _localimpl() # _local_impl属性对应的是_localimpl类的实例
impl.localargs = (args, kw) # _local_impl属性即_localimpl类的实例 的 localargs属性是一个元组
impl.locallock = RLock() # pass 不看
object.__setattr__(self, '_local__impl', impl)
# 把_local__impl 增加给local(), 所以:local()._local__impl is ipml 即 _localimp() # __slots__规定了local()有两个属性,这里已经设置了一个_local__impl;
# 第二个属性__dict__当我们以后在访问的时候使用上下文进行临时增加,比如第85行 impl.create_dict() # 就是local._local__impl.create_dict()
return self # 返回这个配置好_local__impl属性的local()实例 def __getattribute__(self, name): # 当我们取local()的属性时
with _patch(self): # 会通过上下文先把数据准备好
return object.__getattribute__(self, name) # 在准备好的数据中去拿要拿的属性name def __setattr__(self, name, value):
if name == '__dict__': # 这个判断语句是控制local()实例的__dict__属性只能读不能被替换
raise AttributeError(
"%r object attribute '__dict__' is read-only"
% self.__class__.__name__)
with _patch(self): # 同理, 通过上下文先把__dict__构造好
return object.__setattr__(self, name, value) # 然后调用基类的方法设置属性 def __delattr__(self, name): # 删除属性,同理,和__setattr__手法相似
if name == '__dict__': # 这个判断语句是控制local()实例的__dict__属性只能读不能被替换
raise AttributeError(
"%r object attribute '__dict__' is read-only"
% self.__class__.__name__)
with _patch(self): # 同理, 通过上下文先把__dict__构造好
return object.__delattr__(self, name) # 整体架构图:
''' / —— key 属性
/ —— dicts 属性, 格式{id(Thread):(ref(Thread), 线程小字典)}
———— : _local__impl属性 ---------- 是_local类的实例 |
/ —— 其他属性... |
/ /—————————————————————————————————————————————————————————————————————————————————/
创建一个local实例 /
\ /
\ /
———— : __dict__属性 -------- 对应的是_local__impl属性的dicts 中的线程小字典 '''

python语言线程标准库threading.local源码解读的更多相关文章

  1. python线程threading.Timer源码解读

    threading.Timer的作用 官方给的定义是: """Call a function after a specified number of seconds: t ...

  2. threading.local()源码分析

    前段时间写了个多线程的程序,了解到Python中有个与众不同的thread.local()方法,可以创建一个全局对象,各个线程可以用这个全局对象保存各自的局部变量,而在使用时不受其他线程的影响.于是抽 ...

  3. go标准库-log包源码学习

    log包是go语言提供的一个简单的日志记录功能,其中定义了一个结构体类型 Logger,是整个包的基础部分,包中的其他方法都是围绕这整个结构体创建的. Logger结构 Logger结构的定义如下: ...

  4. 如何判断一个Http Message的结束——python源码解读

    HTTP/1.1 默认的连接方式是长连接,不能通过简单的TCP连接关闭判断HttpMessage的结束. 以下是几种判断HttpMessage结束的方式: 1.      HTTP协议约定status ...

  5. Python常用的标准库以及第三方库

    Python常用的标准库以及第三方库有哪些?   20个必不可少的Python库也是基本的第三方库 读者您好.今天我将介绍20个属于我常用工具的Python库,我相信你看完之后也会觉得离不开它们.他们 ...

  6. 一文说透 Go 语言 HTTP 标准库

    本篇文章来分析一下 Go 语言 HTTP 标准库是如何实现的. 转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/561 ...

  7. Python内置模块与标准库

    Python内置模块就是标准库(模块)吗?或者说Python的自带string模块是内置模块吗? 答案是:string不是内置模块,它是标准库.也就是说Python内置模块和标准库并不是同一种东西. ...

  8. Python OS模块标准库的系统接口及操作方法

    Python OS模块标准库的系统接口及操作方法 os.name 返回当前操作系统名,定义了'posix','nt','mac','os2','ce','java'(我使用win7/python3.1 ...

  9. nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模块的Javascript

    Javascrip本身不带IO功能,nodejs选择JavaScript作为开发语言,是因为一般的开发语言的标准库都是带有IO模块的,并且通常这个 模块是阻塞性的,所以nodejs选择了没有自带IO模 ...

随机推荐

  1. Spring Boot (十五): 优雅的使用 API 文档工具 Swagger2

    1. 引言 各位在开发的过程中肯定遇到过被接口文档折磨的经历,由于 RESTful 接口的轻量化以及低耦合性,我们在修改接口后文档更新不及时,导致接口的调用方(无论是前端还是后端)经常抱怨接口与文档不 ...

  2. API---注册表编程

    首先来认识一下键,项,值吧: 左边有五个根键: HKEY_CLASSES_ROOT HKEY_CURRENT_USER HKEY_LOCAL_MACHINE HKEY_USER HKEY_CURREN ...

  3. opencv::轮廓发现(find contour in your image)

    轮廓发现(find contour) 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法. 所以边缘提取的阈值选定会影响最终轮廓发现结果 //发现轮廓 cv::findContours( InputO ...

  4. 阻塞IO模型

    #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> # ...

  5. 一、如何使用postman做接口测试笔记一

    一.什么是接口测试 前端(客户端):Android.ios.web 后端(服务端):java.js.css 接口测试即功能测试,接口是用来连接客户端和服务端的,一般接口返回的数据都是json格式 二. ...

  6. 2019.10.29 CSP%您赛第四场t2

    我太菜了我竟然不会分层图最短路 ____________________________________________________________________________________ ...

  7. Mybatis总结之如何自动生成数据库表结构

    一般情况下,用Mybatis的时候是先设计表结构再进行实体类以及映射文件编写的,特别是用代码生成器的时候. 但有时候不想用代码生成器,也不想定义表结构,那怎么办? 这个时候就会想到Hibernate, ...

  8. 使用maven替换项目依赖中的字节码

    问题描述 我们偶尔会发现一些开源项目的问题,或者出于其他原因,想在某个dependency的代码中加几行或者删除几行来达到目的. 我这里遇到一个dubbo 2.7.3和open feign冲突的问题 ...

  9. (day28)操作系统发展史+进程

    目录 一.操作系统发展史 (一)穿孔卡片(手工操作) (二)批处理系统(磁带存储) 1. 联机批处理系统 2. 脱机批处理系统 (三)多道技术 二.进程 (一)程序和进程 (二)进程调度 1. 先来先 ...

  10. unityweb Request请求

    UnityWebRequest是新的网络请求Api,分为LLApi和HLApi,其中LLApi为低级api,所谓低级api是指只是提供最基本的api接口,然后需要通过不同的参数来确定请求方式.为此un ...