Flask源码剖析(1)

1 预备知识

flask源码很pythonic,使用了很多python的内置方法和面向对象的思想,为了很好的理解flask源码,需要具备一些基础知识

1.1 setattr、getattr、delattr

class Foo:
    storage = {'A':'a','B':'b'}

    def __getattr__(self,item):
        print("__getattr__被执行了")
        return Foo.storage[item]

    def __setattr__(self, key, value):
        print("__setattr__被执行了")
        Foo.storage[key] = value

    def __delattr__(self, item):
        print("__delattr__被执行了")
        del Foo.storage[item]

foo = Foo()
print(foo.A)
foo.C = 'c'
print(Foo.storage)
del foo.C
print(Foo.storage)

"""
OUT:
__getattr__被执行了
a
__setattr__被执行了
{'A': 'a', 'B': 'b', 'C': 'c'}
__delattr__被执行了
{'A': 'a', 'B': 'b'}
从代码的输出结果看:
(1) obj.attr 执行obj.__class__的__getattr__方法
(2) obj.attr=val 执行obj.__class__的__setattr__方法
(3) del obj.attr 执行obj.__class__的__delattr__方法
"""

1.2 python新式类的超能力

最简单的python类

class Foo_(object):
    def __init__(self,storage):
        self.storage = storage


foo = Foo_('hello')
print(foo.storage)
print(foo.__dict__)
foo.A = 'a'
print(foo.A)
print(foo.__dict__)


"""
OUT:

hello
{'storage': 'hello'}
a
{'storage': 'hello', 'A': 'a'}

看到这个输出可能有一个疑问:为什么没有A属性,foo还是可以使用点号设置值和取值?留着这个疑问看下面的代码
"""

”新式类“默认继承object基类


class Foo_(object):
    def __init__(self,val):
        object.__setattr__(self,'storage',val)

foo = Foo_('测试新式类的隐藏功能')
print(foo.storage)

"""
OUT:
测试新式类的隐藏功能
"""

再看第二个实例


class Hi:
    pass

h = Hi()

class Foo__:
    def __init__(self,val):
        object.__setattr__(h,'storage',val)
     
foo = Foo__('test_Hi')
print(h.storage)

"""
OUT:
test_Hi
"""
    

通过这两个例子对比,可以得出以下结论:

  • 任何继承object的类,都有一些隐藏的“超能力”
  • 当执行obj.attr的时候,会现在obj的属性中查找,然后在在obj的class有没有实现getattr方法,最后会执行object的getattr
  • 当执行obj.attr=val的时候,与上面是一样的

1.3 实例初始化中的递归问题

class Foo:
    def __init__(self,storage={'A':'a'}):
        self.storage = storage

    def __getattr__(self,item):
        print("__getattr__被执行了")
        return self.storage[item]

    def __setattr__(self, key, value):
        print("__setattr__被执行了")
        self.storage[key] = value

    def __delattr__(self, item):
        print("__delattr__被执行了")
        del self.storage[item]

foo = Foo()
"""
OUT:
__getattr__被执行了
__getattr__被执行了
__getattr__被执行了
__getattr__被执行了
__getattr__被执行了
。
。
。
N-1
N
RecursionError: maximum recursion depth exceeded while calling a Python object
"""

为什么我初始化一个实例就会无限递归呢?
我们来分析一下这句代码内部做了什么:

在这里插入图片描述

无限递归就出现在上图中,对于类需要重写getattr、setattr等方法,还需要初始化实例属性,可以使用下面这种结构:

class Foo:
    def __init__(self,storage={'A':'a'}):
        object.__setattr__(self,'storage',storage)

    def __getattr__(self,item):
        print("__getattr__被执行了")
        return self.storage[item]

    def __setattr__(self, key, value):
        print("__setattr__被执行了")
        self.storage[key] = value

    def __delattr__(self, item):
        print("__delattr__被执行了")
        del self.storage[item]

foo = Foo()
print(foo.A)
foo.B='b'
print(foo.storage)
del foo.B
print(foo.storage)
"""
OUT:
__getattr__被执行了
a
__setattr__被执行了
{'A': 'a', 'B': 'b'}
__delattr__被执行了
{'A': 'a'}
"""

1.4 偏函数

from functools import partial

def request(name):
    print(name)

method = partial(request, 'method')
args = partial(request,'args')

print(method)
method()
print(args)
args()

print([callable(i) for i in [method,args]])

"""
OUT:
functools.partial(<function request at 0x0000025171926160>, 'method')
method
functools.partial(<function request at 0x0000025171926160>, 'args')
args
[True, True]
"""

partial返回一个指定参数的函数

1.5 代理设计模式

使用一个代理类来代理一个类,所有的操作都由代理类完成

class Foo:
    def __init__(self,a):
        self.a =a
        self.b =b

    def get_a(self):
        return self.a

    def set_a(self,val):
        self.a = val
        
class Foo_Proxy:
    def __init__(self,obj):
        self._foo = obj

    def get_a(self):
        return self._foo.get_a()

    def set_a(self,val):
        self._foo.set_a(val)

proxy = Foo_Proxy(Foo(1))

print(proxy.get_a())
proxy.set_a(2)
print(proxy.get_a())
"""
OUT:
1
2
通过代理对象对基本对象进行操作
"""

1.6 本地线程

flask的上下文管理机制使用的是本地线程技术,上下文是flask的核心,flask与其他框架相比,没有把请求与响应做为视图函数的形参进行传递,而是以一种全局变量的方式随用随取,一定程度上造就了flask的轻

先看一个简单的多线程例子

from threading import Thread
from time import sleep

i = 0

def task(val):
    global i
    sleep(2)
    i = val


thread_list = []

for index,i in enumerate(range(1,11)):
    thread_list.append(Thread(target=task,args=(index,)))


for index,thread in enumerate(thread_list):
    thread.start()
    print(index,i)

for thread in thread_list:
    thread.join()
"""
OUT:
0 10
1 10
2 10
3 10
4 10
5 10
6 10
7 10
8 10
9 10
"""

线程破环了全局变量,每个线程都在争着修改全局变量,可以通过加锁来控制一个时刻只有一个线程拿到这个变量,但是效率会很低

解决这个问题有两种方式:

使用threading.local设置本地线程


from threading import local, Thread, get_ident
from time import sleep

var = local()


def task(data):
    sleep(data)
    var.val = data
    print(f'{get_ident()}:{var.val}')

thread_list = []

for i in range(1,11):
    thread_list.append(Thread(target=task,args=(i,)))

for index,thread in enumerate(thread_list):
    thread.start()


for thread in thread_list:
    thread.join()

"""
OUT:
8412:1
3860:2
17392:3
12568:4
6592:5
16392:6
13336:7
8460:8
10448:9
4016:10
"""

使用全局字段保存每个线程的值

"""
{
	thread_id:
		{
			'key':val
		},
	thread_id:
		{
			'key':val
		},
}
层叠字典的方式,第一层key是线程id,线程id key对应的又是一个字典,字典存储key和value,这就是flask用于存储请求数据的数据结构
"""

2 开始剖析flask

2.1 创建一个最小的flask应用

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Hello world"


if __name__ == '__main__':
    app.run()

(1)

app.run() # 调用了Flask的run方法
from werkzeug.serving import run_simple
	try:
     run_simple(t.cast(str, host), port, self, **options)
# 不看其他代码,执行了werzeug里面的run_simple

(2)

# run_simple调用了make_server函数的serve_forerver方法
srv = make_server(
            hostname,
            port,
            application,
            threaded,
            processes,
            request_handler,
            passthrough_errors,
            ssl_context,
            fd=fd,
        )
        if fd is None:
            log_startup(srv.socket)
srv.serve_forever()

(3)请求进来执行app() 也就是Flask的call方法

    def __call__(self, environ: dict, start_response: t.Callable) -> t.Any
        return self.wsgi_app(environ, start_response)

(4)wagi_app接受了请求参数组成的字段和可调用的响应

    def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
        ctx = self.request_context(environ)
        error: t.Optional[BaseException] = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)
"""
=======================Step1===============================
分析ctx = self.request_context(environ)做了啥
"""
def request_context(self, environ: dict) -> RequestContext:
		# self就是FLask()对象
    return RequestContext(self, environ)

class RequestContext:
	def __init__(
        self,
        app: "Flask", 
        environ: dict,
        request: t.Optional["Request"] = None,
        session: t.Optional["SessionMixin"] = None,
    ) -> None:
"""
ctx是一个RequestContext类的对象
"""
"""
=======================Step2===============================
分析ctx.push()做了啥,
因为ctx是RequestContext类的对象,所以ctx.push()执行的是RequestContext()类的push方法
"""
    def push(self) -> None:
    	# 删掉暂时用不到的代码只留下下面这一句
        _request_ctx_stack.push(self)
      # 通过step3的分析,执行LocalStack类的push方法,请求内容作为参数
"""
=======================Step3===============================
_request_ctx_stack是个啥    
"""
_request_ctx_stack = LocalStack()
# _request_ctx_stack 是LocalStack类的一个对象
class LocalStack:
	def __init__(self) -> None:
		# 使用组合的方式将_local实例化为Local类的一个实例
  	self._local = Local() 
	def push(self, obj: t.Any) -> t.List[t.Any]: # obj为RequestContext类的实例
      """Pushes a new item to the stack"""
      # 这句话会执行Local类的__getattr__方法,空列表做为默认值,复制一个新的到这个列表的引用
      rv = getattr(self._local, "stack", []).copy()
      # 将RequestContext实例append到列表中
      rv.append(obj)
      # 这句话会执行Local类的__setattr__方法
      self._local.stack = rv 
      return rv  # type: ignore 返回push到stack中的值

class Local:
    __slots__ = ("_storage",)
    def __init__(self) -> None:
    	 #根据前面的知识我猜到,这个类肯定实现了__getattr__、__setattr__方法
        object.__setattr__(self, "_storage", ContextVar("local_storage"))
    def __getattr__(self, name: str) -> t.Any:
        # 这句话会执行ContextVar类的get方法
        values = self._storage.get({})
        try:
            return values[name] # 返回values字典中‘stack’健的值
        except KeyError:
            raise AttributeError(name) # 如果没有这个健返回一个异常
    
    def __setattr__(self, name: str, value: t.Any) -> None:
        # self._local.stack = rv name:stack value:rv
        values = self._storage.get({}).copy() # 获取线程ID健的值没有返回一个空字典并复制一个引用为values
        values[name] = value # values['stack'] = rv
        # 这句话会执行ContextVar类的set方法
        self._storage.set(values) # values作为一个嵌套字典存入到一个字典中,键值为当前线程ID

"""
=======================Step4===============================
self._storage是  ContextVar类的一个实例
"""
self._storage = ContextVar("local_storage")#"local_storage"没有实际意义,只是一个名字
    class ContextVar:  # type: ignore
        

        def __init__(self, _name: str) -> None:
            self.storage: t.Dict[int, t.Dict[str, t.Any]] = {}

        def get(self, default: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
            #self.storage初始化是一个字典,通过线程ID健的值没有返回空字典
            return self.storage.get(_get_ident(), default)

        def set(self, value: t.Dict[str, t.Any]) -> None:
            self.storage[_get_ident()] = value   	

"""
=======================Step5(小结)===============================
上部分的代码主要的目的就是将请求信息,push到类似栈结构中
存储模式是:
{
	线程ID1:{'stack':[RequestContext()]},
	线程ID2:{'stack':[RequestContext()]},
	。
	。
	。
	线程IDn:{'stack':[RequestContext()]},
}
"""

2.2 使用请求参数

(1) from flask import request

from flask import Flask,request

app = Flask(__name__)

@app.route('/')
def index():
    if request.method == 'GET':
        return "Hello world"


if __name__ == '__main__':
    app.run()

(2) 请求参数被存储后的使用流程

"""
=======================Step1===============================
先找到request
"""
# request是LocalProxy()类的对象,初始化传入一个函数
request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) 

"""
=======================Step2===============================
_lookup_req_object函数是啥?
看下面的函数先进行总结在分析,这个函数的目的是获取到RequestContext对象的request实例属性
"""

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    """
    ========Step2-1=====
    top = _request_ctx_stack.top执行了啥?
    @property
    def top(self) -> t.Any:
        try:
        	 #"self._local.stack = {} 或者是 {'stack':[RequestContext()]}"
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None
    """
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    return getattr(top, name)

"""
=======================Step3===============================
进入LocalProxy类中
"""
class LocalProxy:
		__slots__ = ("__local", "__name", "__wrapped__")
    def __init__(
        self,
        local: t.Union["Local", t.Callable[[], t.Any]],
        name: t.Optional[str] = None,
    ) -> None:
        object.__setattr__(self, "_LocalProxy__local", local)
        object.__setattr__(self, "_LocalProxy__name", name)
        if callable(local) and not hasattr(local, "__release_local__"):
            object.__setattr__(self, "__wrapped__", local)
"""
==========Step3.1===========
request.method执行的是LocalProxy的getattr方法
    
"""
def _get_current_object(self) -> t.Any:
    if not hasattr(self.__local, "__release_local__"):  # type: ignore
        return self.__local()  # type: ignore
    try:
        return getattr(self.__local, self.__name)  # type: ignore 
    except AttributeError:
        raise RuntimeError(f"no object bound to {self.__name}")  # type: ignore

2.3 LocalProxy的重要性

localproxy可以代理所有的类,代理的作用是对客户端代码与底层代码之间解耦,让代理处理所有的请求而不是直接使用真实的对象

例子1:

from werkzeug.local import LocalStack
user_stack = LocalStack()
# 在堆栈中入栈两个键值对,键都命名为name;
user_stack.push({'name': 'Bob'})
user_stack.push({'name': 'John'})

def get_user():
    # 出栈这两个键值对(获取user对象)
    return user_stack.pop()


# 直接调用函数获取user对象
user = get_user()
# 打印name的value值
print(user['name'])
print(user['name'])

"""
OUT
John
John
"""

例子2

from werkzeug.local import LocalStack, LocalProxy
user_stack = LocalStack()
user_stack.push({'name': 'Bob'})
user_stack.push({'name': 'John'})

def get_user():
    # do something to get User object and return it
    return user_stack.pop()

# 通过LocalProxy代理user对象
user = LocalProxy(get_user)
print(user['name'])
print(user['name'])

"""
OUT:
John
Bob
"""

区别:直接使用LocalStack对象,user一旦赋值就无法再动态更新了,而使用Proxy,每次调用操作符(这里[]操作符用于获取属性),都会重新获取user,从而实现了动态更新user的效果。见下图:
在这里插入图片描述

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kobe_OKOK_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值