python語言線程標準庫threading.local解讀總結(jié)
本段源碼可以學習的地方:
1. 考慮到效率問題,可以通過上下文的機制,在屬性被訪問的時候臨時構建;
2. 可以重寫一些魔術方法,比如 __new__ 方法,在調(diào)用 object.__new__(cls) 前后進行屬性的一些小設置;
3. 在本庫中使用的重寫魔術方法,上下文這兩種基礎之上,我們可以想到函數(shù)裝飾器,類裝飾器,異常捕獲,以及兩種上下文的結(jié)構;
靈活運用這些手法,可以讓我們在代碼架構上更上一層,能夠更加省時省力。
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): # 為當前線程創(chuàng)建一個線程字典,就是(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): # 這個函數(shù)不看 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): # 這個函數(shù)不看 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調(diào)用線程字典管理類的local()._local__impl.get_dict()方法
# 從20行到22這個get_dict()方法的定義可以看出來,拿不到會報KeyError的
except KeyError: # 如果拿不到報 KeyError之后捕捉
dct = impl.create_dict() # 然后再通過線程字典管理類臨時創(chuàng)建一個
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__規(guī)定了local()有兩個屬性,這里已經(jīng)設置了一個_local__impl;
# 第二個屬性__dict__當我們以后在訪問的時候使用上下文進行臨時增加,比如第85行
impl.create_dict() # 就是local._local__impl.create_dict()
return self # 返回這個配置好_local__impl屬性的local()實例
def __getattribute__(self, name): # 當我們?nèi)ocal()的屬性時
with _patch(self): # 會通過上下文先把數(shù)據(jù)準備好
return object.__getattribute__(self, name) # 在準備好的數(shù)據(jù)中去拿要拿的屬性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) # 然后調(diào)用基類的方法設置屬性
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類的實例 |
/ —— 其他屬性... |
/ /—————————————————————————————————————————————————————————————————————————————————/
創(chuàng)建一個local實例 /
\ /
\ /
———— : __dict__屬性 -------- 對應的是_local__impl屬性的dicts 中的線程小字典
'''
以上就是本次介紹的全部知識點內(nèi)容,感謝大家的學習和對腳本之家的支持。
相關文章
Python cookbook(數(shù)據(jù)結(jié)構與算法)從任意長度的可迭代對象中分解元素操作示例
這篇文章主要介紹了Python 數(shù)據(jù)結(jié)構與算法 從任意長度的可迭代象中分解元素操作,結(jié)合實例形式分析了Python使用*表達式針對可迭代對象的分解操作相關實現(xiàn)技巧,需要的朋友可以參考下2018-02-02
PyQt5 QLineEdit輸入的子網(wǎng)字符串校驗QRegExp實現(xiàn)
這篇文章主要介紹了PyQt5 QLineEdit輸入的子網(wǎng)字符串校驗QRegExp實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
Python輸入正負10進制,轉(zhuǎn)4位16進制問題
這篇文章主要介紹了Python輸入正負10進制,轉(zhuǎn)4位16進制問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
Python實現(xiàn)檢測服務器是否可以ping通的2種方法
這篇文章主要介紹了Python實現(xiàn)檢測服務器是否可以ping通的2種方法,本文分別講解了使用ping和fping命令檢測服務器是否可以ping通,需要的朋友可以參考下2015-01-01

