python语言线程标准库threading.local解读总结


Posted in Python onNovember 10, 2019

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

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 相关文章推荐
Python实现的HTTP并发测试完整示例
Apr 23 Python
Python实现对一个函数应用多个装饰器的方法示例
Feb 09 Python
Python+OpenCV目标跟踪实现基本的运动检测
Jul 10 Python
Python一句代码实现找出所有水仙花数的方法
Nov 13 Python
python绘制地震散点图
Jun 18 Python
pytorch使用指定GPU训练的实例
Aug 19 Python
爬虫代理池Python3WebSpider源代码测试过程解析
Dec 20 Python
Python调用scp向服务器上传文件示例
Dec 22 Python
Python任务调度模块APScheduler使用
Apr 15 Python
Python子进程subpocess原理及用法解析
Jul 16 Python
使用Python将语音转换为文本的方法
Aug 10 Python
python3使用diagrams绘制架构图的步骤
Apr 08 Python
Python 脚本拉取 Docker 镜像问题
Nov 10 #Python
Python如何优雅获取本机IP方法
Nov 10 #Python
python argparser的具体使用
Nov 10 #Python
python滑块验证码的破解实现
Nov 10 #Python
Python 中使用 PyMySQL模块操作数据库的方法
Nov 10 #Python
分享PyCharm的几个使用技巧
Nov 10 #Python
Python单元测试与测试用例简析
Nov 09 #Python
You might like
十天学会php(2)
2006/10/09 PHP
百度ping方法使用示例 自动ping百度
2014/01/26 PHP
jquery 经典动画菜单效果代码
2010/01/26 Javascript
Prototype源码浅析 String部分(一)之有关indexOf优化
2012/01/15 Javascript
js/jquery获取浏览器窗口可视区域高度和宽度以及滚动条高度实现代码
2012/12/17 Javascript
浅析JavaScript中两种类型的全局对象/函数
2013/12/05 Javascript
JS之Date对象和获取系统当前时间详解
2014/01/13 Javascript
js实现图片漂浮效果的方法
2015/03/02 Javascript
Jquery检验手机号是否符合规则并根据手机号检测结果将提交按钮设为不同状态
2015/11/26 Javascript
jQuery 的 ready()的纯js替代方法
2016/11/20 Javascript
node.js操作mysql简单实例
2017/05/25 Javascript
判断js数据类型的函数实例详解
2019/05/23 Javascript
vue 中 命名视图的用法实例详解
2019/08/14 Javascript
jquery实现的分页显示功能示例
2019/08/23 jQuery
[05:45]Ti4观战指南(下)
2014/07/07 DOTA
[56:45]DOTA2上海特级锦标赛D组小组赛#1 EG VS COL第一局
2016/02/28 DOTA
python计算方程式根的方法
2015/05/07 Python
Python检测字符串中是否包含某字符集合中的字符
2015/05/21 Python
python3中获取文件当前绝对路径的两种方法
2018/04/26 Python
python使用tcp实现局域网内文件传输
2020/03/20 Python
python flask几分钟实现web服务的例子
2019/07/26 Python
numpy np.newaxis 的实用分享
2019/11/30 Python
利用PyTorch实现VGG16教程
2020/06/24 Python
python如何快速拼接字符串
2020/10/28 Python
详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案
2021/01/29 Python
卡塔尔航空官方网站:Qatar Airways
2017/02/08 全球购物
越南综合购物网站:Lazada越南
2019/06/10 全球购物
英国电气世界:Electrical World
2019/09/08 全球购物
泰国Robinson百货官网:购买知名品牌的商品
2020/02/08 全球购物
阿迪达斯印尼官方网站:adidas印尼
2020/02/10 全球购物
商场总经理岗位职责
2014/02/03 职场文书
部队万能检讨书
2014/02/20 职场文书
学校做一个有道德的人活动方案
2014/08/23 职场文书
2015年国庆节活动总结
2015/03/23 职场文书
少年的你:世界上没有如果,要在第一次就勇敢的反抗
2019/11/20 职场文书
Python创建SQL数据库流程逐步讲解
2022/09/23 Python