Flask系列之源码分析(一)

目录:

  • 涉及知识点
  • Flask框架原理
  • 简单示例
  • 路由系统原理源码分析
  • 请求流程简单源码分析
  • 响应流程简单源码分析
  • session简单源码分析

涉及知识点

1、装饰器

闭包思想

def wapper(func):
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

"""
1. 立即执行wapper函数,并将下面装饰的函数当做参数传递
2. 将wapper函数返回值获取,在index赋值
    index = inner函数
"""
@wapper
def index():
    print('函数内容')

# 实际执行的 inner函数,inner函数内部调用原函数
index()
View Code

[email protected],以上我们知道了python实现闭包,实际是index = inner(index)的封装思想。但不可避免的是inner封装后,会对封装的函数隐藏一些信息。如:包装异常,隐藏异常,打印日志,统计函数使用时间等。@functools.wraps通过update_wrapper函数,用参数wrapped表示的函数对象(例如:square)的一些属性(如:__name__、 __doc__)覆盖参数wrapper表示的函数对象(例如:callf,这里callf只是简单地调用square函数,因此可以说callf是 square的一个wrapper function)的这些相应属性。

import functools
def wapper(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

@wapper
def index():
    print('函数内容')

@wapper
def order():
    print('函数内容')

print(index.__name__)
print(order.__name__)
View Code

2、面向对象封装

class Foo(object):
    def __init__(self,age,name):
        self.age = age
        self.name = name

class Bar(object):
    def __init__(self,counter):
        self.counter = counter
        self.obj = Foo('18','石鹏')

b1 = Bar(1)
print(b1.obj.name)
View Code

3、python对象什么后面可以加括号

- 函数
- 类
- 方法
- 对象
def f1():
    print('f1')

class F2(object):
    pass

class F3(object):
    def __init__(self):
        pass

    def ff3(self):
        print('ff3')

class F4(object):
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('f4')


def func(arg):
    """
    由于arg在函数中加括号,所以他只有4中表现形式:
        - 函数
        - 类
        - 方法
        - 对象
    :param arg:
    :return:
    """
    arg()

# 1. 函数,内部执行函数
func(f1)
# 2. 类,内部执行__init__方法
func(F2)

# 3. 方法,obj.ff3
obj1 = F3()
func(obj1.ff3)

# 4. 对象
obj2 = F4()
func(obj2)
View Code

4、call方法

class F4(object):
    def __init__(self):
        print('构造方法')

    def __call__(self, *args, **kwargs):
        print('f4')

    def run(self,str1):
        print("run:%s" % str1)

obj = F4()
obj()
obj.run('sssss')
View Code

5、函数和方法的区别

在于调用时有没有实例化对象,即跟某个对象关联。

from types import MethodType,FunctionType


class F3(object):
    def __init__(self):
        pass

    def ff3(self):
        print('ff3')

#
v1 = isinstance(F3.ff3,MethodType)  # 方法
v2 = isinstance(F3.ff3,FunctionType) # 函数
print(v1,v2) # False,True

obj = F3()
v1 = isinstance(obj.ff3,MethodType)  # 方法
v2 = isinstance(obj.ff3,FunctionType) # 函数
print(v1,v2) # True False
View Code

Flask框架原理

1、框架本质为通过socket模块实现工作流的请求和响应。

通过socket建立实例,accept等待请求地址,并通过编写路由系统来给予相应的响应。

import socket

def main():
    # 创建老师
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8000))
    sock.listen(5)

    while True:
        # 老师等待 用户请求的到来
        connection, address = sock.accept()
        
        # 获取发送的内容:吴亦凡有没有女朋友?
        buf = connection.recv(1024)

        # 根据请求URL的不同:
        # 回答:没有
        connection.send(b"HTTP/1.1 200 OK\r\n\r\n")
        connection.send(b"No No No")

        # 关闭连接
        connection.close()


if __name__ == '__main__':
    main()

2、flask通过werkzeug模块来帮助我们完成socket性能。

"""
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

@Request.application
def hello(request):
    return Response('Hello World!')

if __name__ == '__main__':
    # 当请求打来之后,自动执行:hello()
    run_simple('localhost', 4000, hello)
"""


from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

class Foo(object):
    def __call__(self, *args, **kwargs):
        return Response('Hello World!')

if __name__ == '__main__':
    # 当请求打来之后,自动执行:hello()
    obj = Foo()
    run_simple('localhost', 4000, obj)
View Code

3、flask快速入门

"""
pip install flask
pip3 install flask
"""

from flask import Flask
# 1. 实例化Flask对象
app = Flask('xxxx')

"""
1. 执行 app.route('/index')并获取返回值 xx
2. 
    @xx
    def index():
        return 'Hello World'
3. 执行 index = xx(index)
本质: 
    {
        '/index': index
    }
"""
@app.route('/index')
def index():
    return 'Hello World'


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

简单示例

1、实现简单登陆

import functools
from flask import Flask,render_template,request,redirect,session

app = Flask('xxxx',template_folder="templates")
app.secret_key = 'as923lrjks9d8fwlkxlduf'


def auth(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        user_info = session.get('user_info')
        if not user_info:
            return redirect('/login')
        return func(*args,**kwargs)
    return inner


"""
{
    /order: inner函数, name: order
    /index: inner函数, name: index
}
"""

@app.route('/order',methods=['GET'])
@auth
def order():
    user_info = session.get('user_info')
    if not user_info:
        return redirect('/login')

    return render_template('index.html')


@app.route('/index',methods=['GET'])
@auth
def index():
    return render_template('index.html')


@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == "GET":
        return render_template('login.html')
    else:
        user = request.form.get('user')
        pwd = request.form.get('pwd')
        if user == 'alex' and pwd == '123':
            session['user_info'] = user
            return redirect('/index')
        # return render_template('login.html',msg = "用户名或密码错误",x =  123)
        return render_template('login.html',**{'msg':'用户名或密码错误'})

@app.route('/logout',methods=['GET'])
def logout():
    del session['user_info']
    return redirect('/login')

if __name__ == '__main__':
    app.run()
app.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>登录页面</h1>
    <form method="post">
        <input type="text" name="user">
        <input type="password" name="pwd">
        <input type="submit" value="提交">{{msg}}
    </form>
</body>
</html>
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>欢迎进入系统</h1>
    <img src="/static/111.png" alt="">
</body>
</html>
templates/index.html

2、flask配置文件

import functools
from flask import Flask

# 配置:模板/静态文件
app = Flask('xxxx',template_folder="templates")
# 配置:secret_key
app.secret_key = 'as923lrjks9d8fwlkxlduf'

# 导入配置文件
app.config.from_object('settings.TestingConfig')


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



if __name__ == '__main__':
    app.run()
View Code
class BaseConfig(object):
    DEBUG = False
    SESSION_REFRESH_EACH_REQUEST = True

class ProConfig(BaseConfig):
    pass

class DevConfig(BaseConfig):
    DEBUG = True

class TestingConfig(BaseConfig):
    DEBUG = True
settings.py

ps.import importlib模块,模块支持传递字符串来导入模块。我们先来创建一些简单模块一遍演示。我们在模块里提供了相同接口,通过打印它们自身名字来区分。可通过importlib.import_module(module_path)来动态导入。其等价于import module_path。

路由系统原理源码分析

1、总体流程:

1.初始化Flask类,Rule类,Map类

2.调用app.route方法

3.route方法调用add_url_rule方法

4. add_url_rule方法rule = self.url_rule_class调用Rule方法,封装url和试图函数

5.add_url_rule方法调用url_map.add(Rule)对路由的rules进行添加[Rule('/index', 函数),]

6.map类存到self.url_map中,Rule存在url_rule_class中

import functools
from flask import Flask,views

# 配置:模板/静态文件
app = Flask('xxxx',template_folder="templates")
"""
{
    '/index': index函数
}

1. decorator = app.route('/index')
2. 
    @decorator
    def index():
        return "index"
3. decorator(index)
"""

"""
Map() = [
    Rule(rule=/index/ endpoint=None  view_func=函数),
]
"""
@app.route('/index')
def index():
    return "index"

"""
Map() = [
    Rule(rule=/index endpoint=None  view_func=函数),
    Rule(rule=/order endpoint=None  view_func=order),
]
"""
def order():
    return 'Order'
app.add_url_rule('/order', None, order)


class TestView(views.View):
    methods = ['GET']
    def dispatch_request(self):
        return 'test!'

app.add_url_rule('/test', view_func=TestView.as_view(name='test'))  # name=endpoint
# app.add_url_rule('/test', view_func=view函数)  # name=endpoint


def auth(func):
    def inner(*args, **kwargs):
        print('before')
        result = func(*args, **kwargs)
        print('after')
        return result
    return inner

class X1View(views.MethodView):
    methods = ['GET','POST']
    decorators = [auth, ]

    def get(self):
        return 'x1.GET'

    def post(self):
        return 'x1.POST'


app.add_url_rule('/x1', view_func=X1View.as_view(name='x1'))  # name=endpoint


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

2、初始化Flask类,Rule类,Map类

#--------------------------------------
# Flask类
class Flask(_PackageBoundObject):
    url_rule_class = Rule
    def __init__(self, import_name, static_path=None, static_url_path=None,
                 static_folder='static', template_folder='templates',
                 instance_path=None, instance_relative_config=False,
                 root_path=None)
        self.url_map = Map()

#--------------------------------------
# Role类
@implements_to_string
class Rule(RuleFactory):
        def __init__(self, string, defaults=None, subdomain=None, methods=None,
                 build_only=False, endpoint=None, strict_slashes=None,
                 redirect_to=None, alias=False, host=None):
        if not string.startswith('/'):
            raise ValueError('urls must start with a leading slash')
        self.rule = string
        self.is_leaf = not string.endswith('/')

        self.map = None
        self.strict_slashes = strict_slashes
        self.subdomain = subdomain
        self.host = host
        self.defaults = defaults
        self.build_only = build_only
        self.alias = alias
        if methods is None:
            self.methods = None
        else:
            if isinstance(methods, str):
                raise TypeError('param `methods` should be `Iterable[str]`, not `str`')
            self.methods = set([x.upper() for x in methods])
            if 'HEAD' not in self.methods and 'GET' in self.methods:
                self.methods.add('HEAD')
        self.endpoint = endpoint
        self.redirect_to = redirect_to

        if defaults:
            self.arguments = set(map(str, defaults))
        else:
            self.arguments = set()
        self._trace = self._converters = self._regex = self._weights = None

#-------------------
#map类
class Map(object):
     default_converters = ImmutableDict(DEFAULT_CONVERTERS)

    def __init__(self, rules=None, default_subdomain='', charset='utf-8',
                 strict_slashes=True, redirect_defaults=True,
                 converters=None, sort_parameters=False, sort_key=None,
                 encoding_errors='replace', host_matching=False):
        self._rules = []
        self._rules_by_endpoint = {}
        self._remap = True
        self._remap_lock = Lock()

        self.default_subdomain = default_subdomain
        self.charset = charset
        self.encoding_errors = encoding_errors
        self.strict_slashes = strict_slashes
        self.redirect_defaults = redirect_defaults
        self.host_matching = host_matching

        self.converters = self.default_converters.copy()
        if converters:
            self.converters.update(converters)

        self.sort_parameters = sort_parameters
        self.sort_key = sort_key

        for rulefactory in rules or ():
            self.add(rulefactory)

3、调用app.route方法

def route(self, rule, **options):
        def decorator(f):
            endpoint = options.pop('endpoint', None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator

4、route方法调用add_url_rule方法

@setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
 if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        options['endpoint'] = endpoint
        methods = options.pop('methods', None)

        # if the methods are not given and the view_func object knows its
        # methods we can use that instead.  If neither exists, we go with
        # a tuple of only ``GET`` as default.
        if methods is None:
            methods = getattr(view_func, 'methods', None) or ('GET',)
        if isinstance(methods, string_types):
            raise TypeError('Allowed methods have to be iterables of strings, '
                            'for example: @app.route(..., methods=["POST"])')
        methods = set(item.upper() for item in methods)

        # Methods that should always be added
        required_methods = set(getattr(view_func, 'required_methods', ()))

        # starting with Flask 0.8 the view_func object can disable and
        # force-enable the automatic options handling.
        provide_automatic_options = getattr(view_func,
            'provide_automatic_options', None)

        if provide_automatic_options is None:
            if 'OPTIONS' not in methods:
                provide_automatic_options = True
                required_methods.add('OPTIONS')
            else:
                provide_automatic_options = False

        # Add the required methods now.
        methods |= required_methods

        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(rule)

5、 add_url_rule方法rule = self.url_rule_class调用Rule方法,封装url和试图函数

add_url_rule,代码同4 ;通过url_rule_class = Rule实例化,代码同2

6、add_url_rule方法调用url_map.add(Rule)对路由的rules进行添加[Rule('/index', 函数),]

add_url_rule代码同4,调用url_map.add方法

    def add(self, rulefactory):
        """Add a new rule or factory to the map and bind it.  Requires that the
        rule is not bound to another map.

        :param rulefactory: a :class:`Rule` or :class:`RuleFactory`
        """
        for rule in rulefactory.get_rules(self):
            rule.bind(self)
            self._rules.append(rule)
            self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
        self._remap = True

7.map类存到self.url_map中,Rule存在url_rule_class中。

同代码2.

请求流程简单源码分析

猜你喜欢

转载自www.cnblogs.com/wangshuyang/p/8854826.html
今日推荐