Flask上下文以及python__setattr__常见问题

Flask上下文管理
-threading.local对象,用于为每个线程开辟一块空间来保存它独有的值。
-源码(request)
    -情况一:单进程单线程,基于全局变量做。
    -情况二:单进程多线程,threading.local对象。
    -情况三:单进程单线程(多协程),threading.lcoal对象做不到。
-建议:
    -以后不支持协程:就用threading.local对象
    -支持协程:自定义类似threading.local对象
 
线程
通过threading.local()保护线程数据安全
 1 import threading
 2 local_values = threading.local()
 3 
 4 '''
 5 local_values = threading.local()就是起到一个锁的作用,每个线程都可以对threading.local对象进行读写,且互相不干扰。
 6 用于为每个线程开辟一块空间来保存它独有的值。
 7 '''
 8 def func(num):
 9     local_values.name=num
10     import time
11     time.sleep(1)
12     print(local_values.name,threading.current_thread().name)
13 
14 for i in range(10):
15     th = threading.Thread(target=func,args=(i,),name="线程是%s"%i)
16     th.start()

 
 
一个简单的自定义方法实现threading.local功能:
线程
 1 import threading
 2 from _thread import get_ident
 3 '''获取线程唯一标示'''
 4 class LOCAL(object):
 5     '''
 6     通过自定义的方式实现threadlocal的功能
 7     初始化的时候定义一个字典以及线程唯一标示
 8     然后通过set设置k,v,再用get取值
 9     '''
10     def __init__(self):
11         self.storage = {}
12         self.get_ident = get_ident
13     def set(self,k,v):
14         ident = self.get_ident()
15         origin = self.storage.get(ident)
16         if not origin:
17             origin = {k:v}
18         else:
19             origin[k] = v
20         self.storage[ident] = origin
21     def get(self,k):
22         ident = self.get_ident()
23         origin = self.storage.get(ident)
24         if not origin:
25             return  None
26         return origin.get(k,None)
27 
28 local_values = LOCAL()
29 def task(num):
30     local_values.set('name',num)
31     import time
32     time.sleep(1)
33     print(local_values.get('name'), threading.current_thread().name)
34 for i in range(10):
35     th = threading.Thread(target=task,args=(i,),name="线程是%s"%i)
36     th.start()
通过安装gevent,用getcurrent的方式获取协程的唯一标示:
以下是实现协程的单独资源管理
 1 import threading
 2 from greenlet import getcurrent as get_ident
 3 '''获取协程唯一标示,需要安装gevent,同时自动安装greenlet,通过
 4 from greenlet import getcurrent 模块判断协程唯一标示,其他代码不变'''
 5 class LOCAL(object):
 6     '''
 7     通过自定义的方式实现threadlocal的功能
 8     初始化的时候定义一个字典以及线程唯一标示
 9     然后通过set设置k,v,再用get取值
10     '''
11     def __init__(self):
12         self.storage = {}
13         self.get_ident = get_ident
14     def set(self,k,v):
15         ident = self.get_ident()
16         origin = self.storage.get(ident)
17         if not origin:
18             origin = {k:v}
19         else:
20             origin[k] = v
21         self.storage[ident] = origin
22     def get(self,k):
23         ident = self.get_ident()
24         origin = self.storage.get(ident)
25         if not origin:
26             return  None
27         return origin.get(k,None)
28 
29 local_values = LOCAL()
30 def task(num):
31     local_values.set('name',num)
32     import time
33     time.sleep(1)
34     print(local_values.get('name'), threading.current_thread().name)
35 for i in range(10):
36     th = threading.Thread(target=task,args=(i,),name="线程是%s"%i)
37     th.start()
 
 
面向对象知识点补充
关于__setattr__的使用以及常见问题:
 1 #扩展内容,__setattr__
 2 class Foo(object):
 3     def set(self,k,v):
 4         pass
 5     def __setattr__(self, key, value):
 6         print(key,value)
 7         pass
 8 
 9 obj = Foo()
10 obj.set('x',123)
11 obj.x = 123 #用__setattr__比set函数要方便许多
12 
13 #__setattr__方法常见的坑
14 
15 class Foo(object):
16     def __init__(self):
17         self.storage = {}
18     def __setattr__(self, key, value):
19         self.storage={'k1':'v1'}
20         print(key,value)
21     def __getattr__(self, item):
22         print(item)
23 
24 
25 obj = Foo()
26 obj.x = 123
27 '''
28 当初始化的时候,self.storage,对象调用storage就会自动执行__setattr__方法,
29 然后__setattr__方法里面又是对象调用属性,就会再执行setattr,这样就是无限递归了。
30 为了避免这个问题需要用下面这种方式实现:
31 '''
32 class Foo(object):
33     def __init__(self):
34         object.__setattr__(self,'storage',{})
35 
36     def __setattr__(self, key, value):
37         self.storage={'k1':'v1'}
38         print(key,value)
39 
40     def __getattr__(self, item):
41         print(item)
42         return "sdf"
43 obj = Foo()
44 #注意如果obj.x = 123就会触发__setattr__方法,还是会出现递归的问题。
以下为参考源码实现的方式:
 1 #源码的实现方式:
 2 import threading
 3 try:
 4     from greenlet import getcurrent as get_ident # 协程
 5 except ImportError:
 6     try:
 7         from thread import get_ident
 8     except ImportError:
 9         from _thread import get_ident # 线程
10 
11 
12 class Local(object):
13 
14     def __init__(self):
15         object.__setattr__(self, '__storage__', {})
16         object.__setattr__(self, '__ident_func__', get_ident)
17 
18 
19     def __getattr__(self, name):
20         try:
21             return self.__storage__[self.__ident_func__()][name]
22         except KeyError:
23             raise AttributeError(name)
24 
25     def __setattr__(self, name, value):
26         ident = self.__ident_func__()
27         storage = self.__storage__
28         try:
29             storage[ident][name] = value
30         except KeyError:
31             storage[ident] = {name: value}
32 
33     def __delattr__(self, name):
34         try:
35             del self.__storage__[self.__ident_func__()][name]
36         except KeyError:
37             raise AttributeError(name)
38 
39 
40 local_values = Local()
41 
42 
43 def task(num):
44     local_values.name = num
45     import time
46     time.sleep(1)
47     print(local_values.name, threading.current_thread().name)
48 
49 
50 for i in range(20):
51     th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
52     th.start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/ArmoredTitan/p/8887019.html