本段源碼可以學習的地方: 1. 考慮到效率問題,可以通過上下文的機制,在屬性被訪問的時候臨時構建; 2. 可以重寫一些魔術方法,比如 __new__ 方法,在調用 object.__new__(cls) 前後進行屬性的一些小設置; 3. 在本庫中使用的重寫魔術方法,上下文這兩種基礎之上,我們可以想... ...
本段源碼可以學習的地方:
1. 考慮到效率問題,可以通過上下文的機制,在屬性被訪問的時候臨時構建;
2. 可以重寫一些魔術方法,比如 __new__ 方法,在調用 object.__new__(cls) 前後進行屬性的一些小設置;
3. 在本庫中使用的重寫魔術方法,上下文這兩種基礎之上,我們可以想到函數裝飾器,類裝飾器,異常捕獲,以及兩種上下文的結構;
靈活運用這些手法,可以讓我們在代碼架構上更上一層,能夠更加省時省力。
1 from weakref import ref # ref用在了構造大字典元素元組的第一個位置即 (ref(Thread), 線程字典) 2 from contextlib import contextmanager # 上下文管理,用來確保__dict__屬性的存在 3 from threading import current_thread, RLock 4 __all__ = ["local"] 5 6 class _localimpl: # local()._local__impl = _localimpl() # local()實例的屬性_local__impl就是這個類的實例 7 """一個管理線程字典的類""" 8 __slots__ = 'key', 'dicts', 'localargs', 'locallock', '__weakref__' # _local__impl有這麼多屬性 9 10 def __init__(self): 11 # 這個self.key是用線上程對象的字典中的key 12 # self.key使用的一個字元串,這樣既能運行的快, 13 # 但是通過'_threading_local._localimpl.' + str(id(self)也能保證不會衝突別的屬性 14 15 self.key = '_threading_local._localimpl.' + str(id(self)) 16 # 17 self.dicts = {} # 大字典 18 # 格式是: { id(線程1):(ref(Thread), 線程1自身的字典), id(線程2):(ref(Thread), 線程2自身的字典), ... } 19 20 def get_dict(self): # 從大字典中拿(ref(Thread), 線程字典), 然後取線程字典 21 thread = current_thread() 22 return self.dicts[id(thread)][1] 23 24 def create_dict(self): # 為當前線程創建一個線程字典,就是(ref(Thread), 線程字典)[1],即元組的第二部分 25 localdict = {} 26 key = self.key # key使用'_threading_local._localimpl.' + str(id(self) 27 thread = current_thread() # 當前線程 28 idt = id(thread) # 當前線程的id 29 def local_deleted(_, key=key): # 這個函數不看 pass 30 # When the localimpl is deleted, remove the thread attribute. 31 thread = wrthread() 32 if thread is not None: 33 del thread.__dict__[key] 34 def thread_deleted(_, idt=idt): # 這個函數不看 pass 35 # When the thread is deleted, remove the local dict. 36 # Note that this is suboptimal if the thread object gets 37 # caught in a reference loop. We would like to be called 38 # as soon as the OS-level thread ends instead. 39 local = wrlocal() 40 if local is not None: 41 dct = local.dicts.pop(idt) 42 wrlocal = ref(self, local_deleted) 43 wrthread = ref(thread, thread_deleted) # 大字典中每一個線程對應的元素的第一個位置: (ref(Thread), 小字典) 44 thread.__dict__[key] = wrlocal 45 self.dicts[idt] = wrthread, localdict # 在大字典中構造: id(thread) : (ref(Thread), 小字典) 46 return localdict 47 48 49 @contextmanager 50 def _patch(self): 51 impl = object.__getattribute__(self, '_local__impl') # 此時的self是local(), 拿local()._local__impl 52 try: 53 dct = impl.get_dict() # 然後從拿到的local()._local__impl調用線程字典管理類的local()._local__impl.get_dict()方法 54 # 從20行到22這個get_dict()方法的定義可以看出來,拿不到會報KeyError的 55 56 except KeyError: # 如果拿不到報 KeyError之後捕捉 57 dct = impl.create_dict() # 然後再通過線程字典管理類臨時創建一個 58 args, kw = impl.localargs # 這個時候把拿到 59 self.__init__(*args, **kw) 60 with impl.locallock: # 通過上下文的方式上鎖 61 object.__setattr__(self, '__dict__', dct) # 給local() 實例增加__dict__屬性,這個屬性指向大字典中value元組的第二個元素,即線程小字典 62 yield # 到目前為止,local()類的兩個屬性都構造完成 63 64 65 class local: # local類 66 __slots__ = '_local__impl', '__dict__' # local類有兩個屬性可以訪問 67 68 def __new__(cls, *args, **kw): 69 if (args or kw) and (cls.__init__ is object.__init__): # pass不看 70 raise TypeError("Initialization arguments are not supported") 71 self = object.__new__(cls) # pass不看 72 impl = _localimpl() # _local_impl屬性對應的是_localimpl類的實例 73 impl.localargs = (args, kw) # _local_impl屬性即_localimpl類的實例 的 localargs屬性是一個元組 74 impl.locallock = RLock() # pass 不看 75 object.__setattr__(self, '_local__impl', impl) 76 # 把_local__impl 增加給local(), 所以:local()._local__impl is ipml 即 _localimp() 77 78 # __slots__規定了local()有兩個屬性,這裡已經設置了一個_local__impl; 79 # 第二個屬性__dict__當我們以後在訪問的時候使用上下文進行臨時增加,比如第85行 80 81 impl.create_dict() # 就是local._local__impl.create_dict() 82 return self # 返回這個配置好_local__impl屬性的local()實例 83 84 def __getattribute__(self, name): # 當我們取local()的屬性時 85 with _patch(self): # 會通過上下文先把數據準備好 86 return object.__getattribute__(self, name) # 在準備好的數據中去拿要拿的屬性name 87 88 def __setattr__(self, name, value): 89 if name == '__dict__': # 這個判斷語句是控制local()實例的__dict__屬性只能讀不能被替換 90 raise AttributeError( 91 "%r object attribute '__dict__' is read-only" 92 % self.__class__.__name__) 93 with _patch(self): # 同理, 通過上下文先把__dict__構造好 94 return object.__setattr__(self, name, value) # 然後調用基類的方法設置屬性 95 96 def __delattr__(self, name): # 刪除屬性,同理,和__setattr__手法相似 97 if name == '__dict__': # 這個判斷語句是控制local()實例的__dict__屬性只能讀不能被替換 98 raise AttributeError( 99 "%r object attribute '__dict__' is read-only" 100 % self.__class__.__name__) 101 with _patch(self): # 同理, 通過上下文先把__dict__構造好 102 return object.__delattr__(self, name) 103 104 # 整體架構圖: 105 ''' 106 107 / —— key 屬性 108 / —— dicts 屬性, 格式{id(Thread):(ref(Thread), 線程小字典)} 109 ———— : _local__impl屬性 ---------- 是_local類的實例 | 110 / —— 其他屬性... | 111 / /—————————————————————————————————————————————————————————————————————————————————/ 112 創建一個local實例 / 113 \ / 114 \ / 115 ———— : __dict__屬性 -------- 對應的是_local__impl屬性的dicts 中的線程小字典 116 117 118 119 '''