threading.local


1. 引子

  • 假如,开了十个线程并且做同样的一件事,他们需要带着自己的数据进来,完成事情后带着自己的数据出去。如果是并发,同时进来,他们的数据就会混乱

  • 一般情况,我们加锁就可以了,一个人先进来,先加锁,另一个人过来看到加锁了,就在外面等,等里面的人出来,自己进去加锁,这样就不会出现数据混乱的问题

  • 另一种解决方法就是使用 threading.local() 来解决

2. 介绍

  • threading.local 会根据线程id为每一个线程创建一块内存空间(即:每一个线程都有一块独立的内存空间进行数据的存储)

  • threading.local 只能为线程开辟空间

3. 作用

  • 为每一个线程创建一个独立的内存空间,使得线程对自己的内存中的数据进行操作,实现了线程与线程之间的数据隔离

4. 锁 和 threading.local 的区别

  • 锁: 是为了让多个线程修改同一份数据

  • threading.local: 为每一个线程创建一块内存空间保存各自的数据

5. 引子的例子

import threading

v = 0


def task(i):
    global v
    v = i
    print(v)  # 打印结果: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9


for i in range(10):
    t = threading.Thread(target=task, args=[i])  # 开启10个线程执行task函数
    t.start()

  • 此时上面的打印结果看不出任何的问题是因为执行的速度太快了,如果执行速度太快,那么它们取到的值有可能是不一样的

  • 只要在打印结果之前执行以下 time.sleep 就可以看到效果了 

import threading
import time

v = 0


def task(i):
    global v
    v = i
    time.sleep(2)
    print(v)  # 打印结果: 9, 9, 9, 9, 9, 9, 9, 9, 9, 9


for i in range(10):
    t = threading.Thread(target=task, args=[i])  # 开启10个线程执行task函数
    t.start()

6. threading.local 的使用

  • 使用 threading.local 解决引子的问题

import threading
import time
from threading import local

local_obj = local()  # 实例化local对象


def task(i):
local_obj.abc = i  # 将值赋值给local对象上的自定义属性上
    time.sleep(2)
    print(local_obj.abc)  # 打印结果: 0, 3, 2, 4, 5, 1, 9, 6, 8, 7 -> 没有按照顺序打印是因为线程的调用顺序是由CPU来决定的


for i in range(10):
    t = threading.Thread(target=task, args=[i])  # 开启10个线程执行task函数
    t.start()

7. 自定义 threading.local 的功能

  • 函数写法

    • 思路: 将 线程唯一标识 作为key,所要保存的值作为 value,存储在字典中

import threading  # 线程模块
import time

DIC = {}

"""
DIC = {
    5104: {'xxx': 2},
    14432: {'xxx': 4},
    13940: {'xxx': 7}
    ……
}
"""


def task(i):
    get_ident = threading.get_ident()  # 获取线程的唯一标识
    if get_ident in DIC:
        DIC[get_ident]['xxx'] = i
    else:
        DIC[get_ident] = {'xxx': i}
    time.sleep(2)
    print(DIC[get_ident]['xxx'])  # 打印结果: 0, 3, 2, 4, 5, 1, 9, 6, 8, 7 -> 没有按照顺序打印是因为线程的调用顺序是由CPU来决定的


for i in range(10):
    t = threading.Thread(target=task, args=[i])
    t.start()

  • 类写法

    • 自定义一个类似于 threading.local 的类,且在这基础上添加了支持协程的功能

    • 思路: 将 线程唯一标识 或 协程唯一标识 作为key,所要保存的值作为 value,存储在字典中

import threading  # 线程模块
import time


# 创建一个类似于 threading.local 的类
class Local(object):
    DIC = {}
"""
    DIC = {
        5104: {'xxx': 2},
        14432: {'xxx': 4},
        13940: {'xxx': 7}
        ……
    }
    """

# 获取线程或协程的唯一标识
    def get_ident(self):
# 如果有协程就优先使用协程,如果没有就使用线程
        try:
            import greenlet  # 因为 greenlet 是第三方模块
            get_ident = greenlet.getcurrent  # 获取协程的唯一标识(其实是一个函数)
        except Exception as e:
            get_ident = threading.get_ident()  # 获取线程的唯一标识
        return get_ident

 # 获取值
    def __getattr__(self, item):
        ident = self.get_ident()
        if ident in self.DIC:
            return self.DIC[ident].get(item)

 # 设置值
    def __setattr__(self, key, value):
        ident = self.get_ident()
        if ident in self.DIC:
            self.DIC[ident][key] = value
        else:
            self.DIC[ident] = {key: value}


local_obj = Local()  # 实例化local对象


def task(i):
    local_obj.abc = i
    time.sleep(2)
    print(local_obj.abc)  # 打印结果: 0, 3, 2, 4, 5, 1, 9, 6, 8, 7 -> 没有按照顺序打印是因为线程的调用顺序是由CPU来决定的


for i in range(10):
    t = threading.Thread(target=task, args=[i])
    t.start()

Flask的上下文管理机制的介绍


1. 介绍

  • Flask的上下文管理机制类似于 threading.local

  • Flask的上下文管理机制的写法类似于 threading.local 章节中的 自定义 threading.local 的功能 -> 类写法

  • 根据线程或协程的唯一标识为每一个线程或协程创建一块内存空间(即:每一个线程或协程都有一块独立的内存空间进行数据的存储)

  • Flask的上下文管理机制可以为线程或协程开辟空间

  • 通俗理解上下文管理机制: 每个线程或协程都有自己的内存空间保存自身对应的数据

2. 作用

  • 为每一个线程或协程创建一个独立的内存空间,使得线程或协程对自己的内存中的数据进行操作,实现了(线程/协程与(线程/协程之间的数据隔离

Flask的请求上下文管理机制


1. Flask 请求上下文管理机制的说明

  • 请求上下文包含了: request、session

  • 注意: 请求上下文和app上下文所使用的 Local LocalStack LocalProxy 都是同一个类(即: 请求上下文 和 app上下文所生成的数据都会保存在同一个 __storage__ 字典内)

2. Flask 请求上下文管理机制重要源码分析

  • 类和方法的说明:

    • Local 类: 为每个线程或协程开辟各自的空间储存数据
      • 通俗理解: Local 类创建了一个名为 __storage__ 字典存储着每个线程或协程的数据

    • LocalStack 类: 帮助我们对 __storage__ 字典下的 stack 列表维护成一个栈(即: 后进先出)
      • 通俗理解: 帮助我们对 __storage__ 字典下的 stack 列表进行添加和删除操作

    • RequestContext 类: 存储着request请求信息和session等其他相关方法的类
      • 通俗理解: 实例化一个 RequestContext 类得到一个 ctx 对象,且ctx对象下有request请求信息和session等其他相关方法,然后将该ctx对象保存在 __storage__ 字典下指定线程或协程下的 stack 列表里

    • LocalProxy 类: 对 ctx 对象(即: request_context 对象)下的 request session 进行操作(即: 增删改查)
      • 通俗理解: 调用在 _lookup_req_object 函数的基础上所创建的偏函数获取到 ctx 对象,然后对 ctx 对象(即: request_context 对象)下的 request session 进行操作(即: 增删改查)

    • _lookup_req_object 函数: 调用 LocalStack 下的 top 方法获取 __storage__ 下的 ctx 对象(即:request_context对象)中指定的属性(如: request、session)
      • 通俗理解: 通过 _lookup_req_object 函数可以获取到保存在 __storage__ 字典下指定线程或协程下的 stack 列表里 ctx 对象中的指定属性

  • 所存储的数据结构

__storage__ = {
    '线程或协程的唯一标识': {'stack': [ctx, xxx, xxx]},
    '线程或协程的唯一标识': {'stack': [ctx, xxx, xxx]},
    '线程或协程的唯一标识': {'stack': [ctx, xxx, xxx]},
……
}

  • 查看重要源码

from flask import globals

  • 重要的源代码

# f.py

import functools

# 如果有协程就使用协程,如果没有则使用线程
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


# 为每个线程或协程开辟各自的空间储存数据
class Local(object):
    __slots__ = ("__storage__", "__ident_func__")  # 设置对象允许访问的变量或方法

    def __init__(self):
 # 调用 object.__setattr__ 方法,先将 __storage__ 和 __ident_func__ 的属性值添加到 Local 类中,然后 __setattr__ 或 __delattr__ 就可以进行调用
        object.__setattr__(self, "__storage__", {})  # __storage__ 用于存储各个线程或协程的数据的字典
 """
            __storage__ 初始化的数据结构
            __storage__ = {}
            __storage__ 添加数据后的数据结构
            __storage__ = {
                '线程或协程的唯一标识': {key: value, ……},
                '线程或协程的唯一标识': {key: value, ……},
                '线程或协程的唯一标识': {key: value, ……},
                ……
            }
        """
        object.__setattr__(self, "__ident_func__", get_ident)  # __ident_func__ -> 获取线程或协程的唯一标记的方法

# 获取 __storage__ 中的值
    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

# 添加或修改 __storage__ 中的值
    def __setattr__(self, name, value):
        ident = self.__ident_func__()  # 获取协程或线程的唯一标识
        storage = self.__storage__  # 获取用于存储各个线程或协程的数据的字典
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

# 删除 __storage__ 中的值
    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)


# 帮助我们对 __storage__ 字典下的 stack 列表维护成一个栈(即: 后进先出) -> 通俗理解: 帮助我们对 __storage__ 字典下的 stack 列表进行添加和删除操作
class LocalStack(object):
    def __init__(self):
        self._local = Local()  # 实例化local对象

    """
        调用该类对 __storage__ 操作后的数据结构
        __storage__ = {
            '线程或协程的唯一标识': {'stack': [xxx, xxx, xxx]},
            '线程或协程的唯一标识': {'stack': [xxx, xxx, xxx]},
            '线程或协程的唯一标识': {'stack': [xxx, xxx, xxx]},
            ……
        }
    """

# 往 __storage__ 中的 stack 列表添加值
    def push(self, value):
        rv = getattr(self._local, "stack", None)  # 等同于 self._local.stack -> 执行 Local 类中的 __getattr__ 方法
        if rv is None:
            self._local.stack = rv = []  # 执行 Local 类中的 __setattr__ 方法
        rv.append(value)
        return rv

# 删除 __storage__ 中的 stack 列表的值,如果 stack 列表只有一个则不进行删除,当请求结束的时候就会调用该方法删除当前线程中的指定 ctx 对象
    def pop(self):
        stack = getattr(self._local, "stack", None)  # 等同于 self._local.stack -> 执行 Local 类中的 __getattr__ 方法
        if stack is None:
            return None
        elif len(stack) == 1:
# release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()

# 获取 __storage__ 中的 stack 列表的值
    @property
    def top(self):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None


# 代理 -> 对 ctx/app_ctx 对象(即: request_context/app_context 对象)下的 request session g app 进行操作(即: 增删改查)
class LocalProxy(object):
    __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")

    def __init__(self, local, name=None):
"""
        :param local:  偏函数
        :param name:  request、session、g、None
        """

        """
            object.__setattr__(self, "_LocalProxy__local", local) 的解释:
                1. 将 local 赋值给 _LocalProxy__local 属性,该属性可以通过 self.__local 进行访问
                2. _LocalProxy__local属性名 等于 __local 属性名
                3. 因为解释器会将 __local 替换为 _classname__local属性名 为确保名称不会与另一个类中的类似名称重叠的方式
        """
        object.__setattr__(self, "_LocalProxy__local", local)  # 将 local 赋值给 _LocalProxy__local 属性,该属性可以通过 self.__local 进行访问
        object.__setattr__(self, "__name__", name)  # 将 name 赋值给 __name__ 属性
        if callable(local) and not hasattr(local, "__release_local__"):
            object.__setattr__(self, "__wrapped__", local)

# 调用偏函数获取 ctx 对象(即: request_context 对象)
    def _get_current_object(self):
        if not hasattr(self.__local, "__release_local__"):
            return self.__local()  # 调用偏函数获取 ctx 对象(即: request_context 对象)
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError("no object bound to %s" % self.__name__)

# 当 LocalProxy 所实例化的对象执行了 对象.属性名 的时候就会执行该方法
    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)  # 调用偏函数获取 ctx 对象(即: request_context 对象),然后使用反射获取 ctx 对象下的对应 request、session、g

# 当 LocalProxy 所实例化的对象执行了 对象.属性名=xxx 的时候就会执行该方法
    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
# 当 LocalProxy 所实例化的对象执行了 del 对象.属性名 的时候就会执行该方法
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
# 当 LocalProxy 所实例化的对象执行了 print/str(对象.属性名) 的时候就会执行该方法
    __str__ = lambda x: str(x._get_current_object())


# request 类
class Request(object):
    def __init__(self):
        self.method = 'GET'
        self.form = 123


# session 类
class Session(object):
    def __init__(self):
        self.name = 'Kevin'
        self.age = 18


# 一个拥有request和session信息的类
class RequestContext(object):
    def __init__(self):
        self.request = Request()
        self.session = Session()


_request_ctx_stack = LocalStack()  # 实例化 LocalStack 得到 _request_ctx_stack 对象
request_context = RequestContext()  # 实例化一个 request_context 对象,里面拥有 request 和 session 等属性
_request_ctx_stack.push(request_context)  # 将 request_context 对象,添加到 __storage__ 中的 stack 列表中


# 调用 LocalStack 下的 top 方法获取 __storage__ 下的 request_context 对象,然后根据传入的属性名通过反射获取 request_context 对象下对应的属性值
def _lookup_req_object(name):
    ctx = _request_ctx_stack.top  # 调用 LocalStack 下的 top 方法获取 __storage__ 下的 ctx 对象(即:request_context 对象)
    return getattr(ctx, name)  # 等同于 ctx.request 或者 ctx.session


# 通过 LocalProxy 对 ctx 对象(即: request_context 对象)下的 request session 进行操作(即: 增删改查)
request = LocalProxy(functools.partial(_lookup_req_object, 'request'))
session = LocalProxy(functools.partial(_lookup_req_object, 'session'))

  • 获取 request 或 session 的方式一

    • 在视图函数中也可以通过 flask.globals._request_ctx_stack 中的 _request_ctx_stack 进行获取

# v.py

from f import _request_ctx_stack

request = _request_ctx_stack.top.request
print(request.method)  # GET

session = _request_ctx_stack.top.session
print(session.name)  # Kevin

  • 获取 request 或 session 的方式二

# v.py

from f import request, session

print(request.method)  # GET
print(session.name)  # Kevin

3. Flask 请求上下文管理机制流程图

  • 获取 request 或 session 的方式一


  • 获取 request 或 session 的方式二


Flask的app上下文管理机制


1. Flask app上下文管理机制的说明

  • 请求上下文包含了: app、g

  • 注意: app上下文和请求上下文所使用的 Local LocalStack LocalProxy 都是同一个类(即: 请求上下文 和 app上下文所生成的数据都会保存在同一个 __storage__ 字典内)

2. Flask 请求上下文管理机制重要源码分析

  • 类和方法的说明:

    • Local 类: 为每个线程或协程开辟各自的空间储存数据
      • 通俗理解: Local 类创建了一个名为 __storage__ 字典存储着每个线程或协程的数据

    • LocalStack 类: 帮助我们对 __storage__ 字典下的 stack 列表维护成一个栈(即: 后进先出)
      • 通俗理解: 帮助我们对 __storage__ 字典下的 stack 列表进行添加和删除操作

    • AppContext 类: 存储着app和g等其他相关方法的类
      • 通俗理解: 实例化一个 AppContext 类得到一个 app_ctx 对象,且app_ctx对象下有app和g等其他相关方法,然后将该app_ctx对象保存在 __storage__ 字典下指定线程或协程下的 stack 列表里

    • LocalProxy 类: 对 app_ctx 对象(即: app_context 对象)下的 g app 进行操作(即: 增删改查)
      • 通俗理解: 调用在 _lookup_app_object 函数的基础上所创建的偏函数获取到 app_ctx 对象,然后对 app_ctx 对象(即: app_context 对象)下的 g 进行操作(即: 增删改查)
      • 通俗理解: 调用 _find_app 函数获取到 app_ctx 对象,然后对 app_ctx 对象(即: app_context 对象)下的 app 进行操作(即: 增删改查)

    • _lookup_app_object 函数: 调用 LocalStack 下的 top 方法获取 __storage__ 下的 app_ctx 对象(即:app_context对象)中指定的属性(如: g 等)
      • 通俗理解: 通过 _lookup_app_object 函数可以获取到保存在 __storage__ 字典下指定线程或协程下的 stack 列表里 app_ctx 对象中的指定属性

    • _find_app 函数: 调用 LocalStack 下的 top 方法获取 __storage__ 下的 app_ctx 对象(即:app_context对象)中指定的属性 app
      • 通俗理解: 通过 _find_app 函数可以获取到保存在 __storage__ 字典下指定线程或协程下的 stack 列表里 app_ctx 对象中的指定属性 app

  • 所存储的数据结构

__storage__ = {
    '线程或协程的唯一标识': {'stack': [app_ctx, xxx, xxx]},
    '线程或协程的唯一标识': {'stack': [app_ctx, xxx, xxx]},
    '线程或协程的唯一标识': {'stack': [app_ctx, xxx, xxx]},
……
}

  • 查看重要源码

from flask import globals

  • 重要的源代码

# f.py

import functools

# 如果有协程就使用协程,如果没有则使用线程
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


# 为每个线程或协程开辟各自的空间储存数据
class Local(object):
    __slots__ = ("__storage__", "__ident_func__")  # 设置对象允许访问的变量或方法

    def __init__(self):
 # 调用 object.__setattr__ 方法,先将 __storage__ 和 __ident_func__ 的属性值添加到 Local 类中,然后 __setattr__ 或 __delattr__ 就可以进行调用
        object.__setattr__(self, "__storage__", {})  # __storage__ 用于存储各个线程或协程的数据的字典
 """
            __storage__ 初始化的数据结构
            __storage__ = {}
            __storage__ 添加数据后的数据结构
            __storage__ = {
                '线程或协程的唯一标识': {key: value, ……},
                '线程或协程的唯一标识': {key: value, ……},
                '线程或协程的唯一标识': {key: value, ……},
                ……
            }
        """
        object.__setattr__(self, "__ident_func__", get_ident)  # __ident_func__ -> 获取线程或协程的唯一标记的方法

# 获取 __storage__ 中的值
    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

# 添加或修改 __storage__ 中的值
    def __setattr__(self, name, value):
        ident = self.__ident_func__()  # 获取协程或线程的唯一标识
        storage = self.__storage__  # 获取用于存储各个线程或协程的数据的字典
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

# 删除 __storage__ 中的值
    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)


# 帮助我们对 __storage__ 字典下的 stack 列表维护成一个栈(即: 后进先出) -> 通俗理解: 帮助我们对 __storage__ 字典下的 stack 列表进行添加和删除操作
class LocalStack(object):
    def __init__(self):
        self._local = Local()  # 实例化local对象

    """
        调用该类对 __storage__ 操作后的数据结构
        __storage__ = {
            '线程或协程的唯一标识': {'stack': [xxx, xxx, xxx]},
            '线程或协程的唯一标识': {'stack': [xxx, xxx, xxx]},
            '线程或协程的唯一标识': {'stack': [xxx, xxx, xxx]},
            ……
        }
    """

# 往 __storage__ 中的 stack 列表添加值
    def push(self, value):
        rv = getattr(self._local, "stack", None)  # 等同于 self._local.stack -> 执行 Local 类中的 __getattr__ 方法
        if rv is None:
            self._local.stack = rv = []  # 执行 Local 类中的 __setattr__ 方法
        rv.append(value)
        return rv

# 删除 __storage__ 中的 stack 列表的值,如果 stack 列表只有一个则不进行删除,当请求结束的时候就会调用该方法删除当前线程中的指定 app_ctx 对象
    def pop(self):
        stack = getattr(self._local, "stack", None)  # 等同于 self._local.stack -> 执行 Local 类中的 __getattr__ 方法
        if stack is None:
            return None
        elif len(stack) == 1:
# release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()

# 获取 __storage__ 中的 stack 列表的值
    @property
    def top(self):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None


# 代理 -> 对 ctx/app_ctx 对象(即: request_context/app_context 对象)下的 request session g app 进行操作(即: 增删改查)
class LocalProxy(object):
    __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")

    def __init__(self, local, name=None):
"""
        :param local:  偏函数
        :param name:  request、session、g、None
        """

        """
            object.__setattr__(self, "_LocalProxy__local", local) 的解释:
                1. 将 local 赋值给 _LocalProxy__local 属性,该属性可以通过 self.__local 进行访问
                2. _LocalProxy__local属性名 等于 __local 属性名
                3. 因为解释器会将 __local 替换为 _classname__local属性名 为确保名称不会与另一个类中的类似名称重叠的方式
        """
        object.__setattr__(self, "_LocalProxy__local", local)  # 将 local 赋值给 _LocalProxy__local 属性,该属性可以通过 self.__local 进行访问
        object.__setattr__(self, "__name__", name)  # 将 name 赋值给 __name__ 属性
        if callable(local) and not hasattr(local, "__release_local__"):
            object.__setattr__(self, "__wrapped__", local)

# 调用偏函数获取 ctx/app_ctx 对象(即: request_context/app_context 对象)
    def _get_current_object(self):
        if not hasattr(self.__local, "__release_local__"):
            return self.__local()  # 调用偏函数获取 ctx/app_ctx 对象(即: request_context/app_context 对象)
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError("no object bound to %s" % self.__name__)

# 当 LocalProxy 所实例化的对象执行了 对象.属性名 的时候就会执行该方法
    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)  # 调用偏函数获取 ctx/app_ctx 对象(即: request_context/app_context 对象),然后使用反射获取 ctx/app_ctx 对象下的对应 request、session、g

# 当 LocalProxy 所实例化的对象执行了 对象.属性名=xxx 的时候就会执行该方法
    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
# 当 LocalProxy 所实例化的对象执行了 del 对象.属性名 的时候就会执行该方法
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
# 当 LocalProxy 所实例化的对象执行了 print/str(对象.属性名) 的时候就会执行该方法
    __str__ = lambda x: str(x._get_current_object())


# 这里的app所指的是通过 Flask 所实例化出来的 app
class App:
    def __init__(self):
        self.config = 'app的配置'


class G:
    def __init__(self):
        self.name = 'Kevin'


# 一个拥有app和g信息的类
class AppContext(object):
    def __init__(self):
        self.app = App()  # 注意: 这里的app所指的是通过 Flask 所实例化出来的 app 对象
        self.g = G()


_app_ctx_stack = LocalStack()  # 实例化 LocalStack 得到 _app_ctx_stack 对象
app_context = AppContext()  # 实例化一个 app_context 对象,里面拥有 app 和 g 等属性
_app_ctx_stack.push(app_context)  # 将 app_context 对象,添加到 __storage__ 中的 stack 列表中


# 调用 LocalStack 下的 top 方法获取 __storage__ 下的 app_context 对象,然后根据传入的属性名通过反射获取 app_context 对象下对应的属性值
def _lookup_app_object(name):
    app_ctx = _app_ctx_stack.top  # 调用 LocalStack 下的 top 方法获取 __storage__ 下的 app_ctx 对象(即:app_context 对象
    return getattr(app_ctx, name)  # 等同于 app_ctx.g


# 调用 LocalStack 下的 top 方法获取 __storage__ 下的 app_context 对象下的 app 对象
def _find_app():
    app_ctx = _app_ctx_stack.top  # 调用 LocalStack 下的 top 方法获取 __storage__ 下的 app_ctx 对象(即:app_context 对象)
    return app_ctx.app


# 通过 LocalProxy 对 app_ctx 对象(即: app_context 对象)下的 app g 进行操作(即: 增删改查)
current_app = LocalProxy(_find_app)
g = LocalProxy(functools.partial(_lookup_app_object, 'g'))

  • 获取 current_app 或 g 的方式一

    • 在视图函数中也可以通过 flask.globals._app_ctx_stack 中的 _app_ctx_stack 进行获取

# v.py

from f import _app_ctx_stack

current_app = _app_ctx_stack.top.app
print(current_app.config)  # app的配置

g = _app_ctx_stack.top.g
print(g.name)  # Kevin

  • 获取 current_app 或 g 的方式二

# v.py

from f import current_app, g

print(current_app.config)  # app的配置
print(g.name)  # Kevin

3. Flask app上下文管理机制流程图

  • 获取 current_app 或 g 的方式一


  • 获取 current_app 或 g 的方式二


上下文管理机制总的流程图


  • 所存储的数据结构

__storage__ = {
    '线程或协程的唯一标识': {'stack': [ctx, app_ctx, xxx]},
    '线程或协程的唯一标识': {'stack': [ctx, app_ctx, xxx]},
    '线程或协程的唯一标识': {'stack': [ctx, app_ctx, xxx]},
……
}

  • 请求和app上下文管理机制总的流程图