第一版:基于函数实现local对象功能
紧接上文,最最直白的实现方式:
import time
from threading import get_ident, Thread
storage ={}defset(k, v):
ident = get_ident()if ident in storage:
storage[ident][k]= v
else:
storage[ident]={k: v}defget(k):
ident = get_ident()return storage[ident][k]deftask(arg):set('val', arg)
time.sleep(2)
v = get('val')print(v)for i inrange(10):
t = Thread(target=task, args=(i,))
t.start()
已经可以实现local对象的功能,但是这样的话使用很不方便!
第二版:基于面向对象思想实现local对象功能
(1)2.1版本:
import time
from threading import get_ident, Thread
classLocal(object):
storage ={}defset(self, k, v):
ident = get_ident()if ident in Local.storage:
Local.storage[ident][k]= v
else:
Local.storage[ident]={k: v}defget(self, k):
ident = get_ident()return Local.storage[ident][k]if __name__ =='__main__':
obj = Local()deftask(arg):
obj.set('val', arg)
time.sleep(2)
v = obj.get('val')print(v)for i inrange(10):
t = Thread(target=task, args=(i,))
t.start()
(2)2.2版本:
- 实现local对象的通过.value进行赋值操作:
import time
from threading import get_ident, Thread
classLocal(object):
storage ={}def__setattr__(self, k, v):
ident = get_ident()if ident in Local.storage:
Local.storage[ident][k]= v
else:
Local.storage[ident]={k: v}def__getattr__(self, k):
ident = get_ident()return Local.storage[ident][k]if __name__ =='__main__':
obj = Local()deftask(arg):
obj.val = arg
time.sleep(2)
v = obj.val
print(v)for i inrange(10):
t = Thread(target=task, args=(i,))
t.start()
(3)2.3版本:
上述代码存在一个很严重的问题:由于storage是定义为Local类的一个类属性,所以不管创建几个Local()对象,用的都是同一个storage来存!
优化点就是:让代码变得可以放同一个storage/不同storage!
如下将storage变为实例属性即可(想放一个storage里就实例化一个Local对象,想放不同storage里就分别实例化对应Local对象即可):
但是上面这会报错,定位到实例化Local对象时报的错,如下:
问题分析:
- 实例化后会触发
init
方法,在里面有个self.storage={}
,就会触发setattr
方法,里面判断走else,所以又触发setattr
…
问题解决:
- 因为Local继承了类object,所以可以使用基类object的
setattr
来触发父类的setattr
来设置storage,这样就在设置实例属性storage的时候绕过了setattr的自定义实现:
分析:
- 上述2.3版本已经实现了threading.local。但是为啥已经有现成的了,我们不直接用,还要自行实现呢?
- 答案很简单,因为threading.local的功能不够用!
比如我们现在要实现为每个协程创建一份空间。上述2.3版本代码只需要更改一处就可以了(下述代码段第三行)!
【greenlet.getcurrent就是获取当前协程的唯一标识】
import time
from threading import Thread
from greenlet import getcurrent as get_ident
classLocal(object):def__init__(self):# self.storage = {}object.__setattr__(self,'storage',{})def__setattr__(self, k, v):
ident = get_ident()if ident in self.storage:
self.storage[ident][k]= v
else:
self.storage[ident]={k: v}def__getattr__(self, k):
ident = get_ident()return self.storage[ident][k]if __name__ =='__main__':
obj = Local()deftask(arg):
obj.val = arg
time.sleep(2)
v = obj.val
print(v)for i inrange(10):
t = Thread(target=task, args=(i,))
t.start()
到现在,我们已经非常牛逼了,因为这就是flask里的源码:
from flask importglobals
classLocal(object):
__slots__ =("__storage__","__ident_func__")def__init__(self):object.__setattr__(self,"__storage__",{})object.__setattr__(self,"__ident_func__", get_ident)def__iter__(self):returniter(self.__storage__.items())def__call__(self, proxy):"""Create a proxy for a name."""return LocalProxy(self, proxy)def__release_local__(self):
self.__storage__.pop(self.__ident_func__(),None)def__getattr__(self, name):try:return self.__storage__[self.__ident_func__()][name]except KeyError:raise AttributeError(name)def__setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name]= value
except KeyError:
storage[ident]={name: value}def__delattr__(self, name):try:del self.__storage__[self.__ident_func__()][name]except KeyError:raise AttributeError(name)
会发现这个源码跟上述2.3版本代码一样哦!!!
版权归原作者 孤寒者 所有, 如有侵权,请联系我们删除。