Local:
在之前一篇博数据库连接池中,提到了local的用法,那么本篇来看看flask中Local类的实现,我们看看它的源码中是怎么实现,以及它的执行过程:
首先我们导入它:它在werkzeug下的local模块中,这里将它的代码粘贴出来 :
#!usr/bin/env python
# *- coding:utf-8 -*-
# Author: Andy
from flask import globals
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
"""
上面这段代码表明,当使用了协程时,get_indent 是从协程中取,即获取协程的id,如果没有使用协程,那么从线程中获取线程的唯一id
下面需要用到这个id,即get_ident
"""
class Local(object):
__slots__ = ("__storage__", "__ident_func__")
# 能通过点号获取的值
def __init__(self):
# 实例化时 self.__storage__ = {}
# self.__ident_func__ = get_ident
object.__setattr__(self, "__storage__", {})
object.__setattr__(self, "__ident_func__", get_ident)
def __iter__(self):
return iter(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)
下面我们分析一下使用local的情况,看看它发生了什么:
实例化:
当我们实例化对象,比如local = Local()
此时执行Local对象的__init__方法. local.__storage__ = {}, local.__ident_func__ = get_ident,
赋值:
现在我对local赋值,local.a = 1, 即执行__setattr__方法, ident = self.__ident_func__()=get_ident(),获取到当前进程的id,假设为1111,
storage = self.__storage__ = {}, storage[ident][name] = value, 如果storage中有键为1111的字典,且1111这个字典中存在为a
的键,则对它赋值。如果不存在storage['1111']={'a':1},结果就是对当前线程id生成一个字曲。
取值:
取值时执行__getattr__方法,self.__storage__[self.__ident_func__()][name] == storage[get_ident()}[name]即 storage['1111']['a']
这样便取到了值1。
__delattr__则是将数据销毁, 同样release_local则是将对应的值从字典中pop出来
整个过程非常清晰明了,与数据库连接池中一样,都是维护一个字典,其中以线程id为键,字典中保存键和什。取值时也根据线程id来获取这个字典中的键值。因为线程(或者协程)的id是唯一的,也就保证了数据和独立。事实上flask内部还有一个LocalStack类。
LocalStack
class LocalStack(object):
def __init__(self):
self._local = Local()
def __release_local__(self):
self._local.__release_local__()
def _get__ident_func__(self):
return self._local.__ident_func__
def _set__ident_func__(self, value):
object.__setattr__(self._local, "__ident_func__", value)
__ident_func__ = property(_get__ident_func__, _set__ident_func__)
del _get__ident_func__, _set__ident_func__
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError("object unbound")
return rv
return LocalProxy(_lookup)
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
stack = getattr(self._local, "stack", None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local)
return stack[-1]
else:
return stack.pop()
@property
def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
def release_local(local):
"""Releases the contents of the local for the current context.
>>> loc = Local()
>>> loc.foo = 42
>>> release_local(loc)
>>> hasattr(loc, 'foo')
False
"""
local.__release_local__()
可以看到它的get_ident, release_local等方法是使用的Local类的方法,这里略过。 现在我们分析它的执行过程 local_stack = Local(),执行它的__init__方法,self._local = Local(), 即执行了上面那个Local类的实例化,也就是也生成了一个字典 现在假如我执行push方法,比如local_stack.push('andy'), stack = getattr(self._local, "stack", None),即stack=getattr(Local(), "stack", None) 取Local类对象的的stack的值,所以执行Local类的getattr方法,第一次很执行,因为__storage__中还没有这个’stack'键,所以出错,取不到值,因为rv=None rv=None, 因此执行:self._local.stack = rv = [] , Local().stack = [],即对字典赋值, {__storage__, {'stack':[]}}=__storage={'stack':[]} 然后将值添加到这个列表中,即:__storage__={'stack':['andy',]} top方法, self._local.stack[-1]=Local().stack[-1],即取stack字典中的值, getattr方法而stack对应一个列表rv,取最后一个值,即栈顶。 pop方法:stack = getattr(Local(), 'stack',None), 去字典里获取 stack,如果第一个pop,则返回None,因为没有stack。 本例中列表里有个 ['andy',], 即stack=['andy',], 因此会执行stack.pop(),即把andy从列表中删除,另外 len(stack) ==1 也成立,releas_local(Local()),release_local是一个独立方法, 但它实例是调用了Local类的__release_local__方法,也就会执行Local类的 __elease_loal__方法,self.__storage__.pop(self.__ident_func__(), None), 可以看到它将当前线程id组成的字典从__storage__中pop掉了,也就是将当前线程的数据销毁 了
可以看到,在Flask内部有两个类,一个Local, 一个LocalStack, Local类的功能和threading.local功能一样,为每个线程开辟空间用来存放数据,其内部实现机制都是维护一个字典,以线程(协程)的id为键,进行数据隔离。而LocalStack类则依赖local类的功能,并将它维护成一个栈