Flask 源码阅读-下篇 |Python 主题月

简介: flask项目大名鼎鼎,不需要多做介绍。我把它称之为python服务开发的TOP2项目,另外一个就是django。这两个项目各有千秋,各自有不同的应用场景,都需要深入理解,熟练掌握。本次源码选择的版本是 1.1.2,我会采用慢读法,尽自己最大努力把它讲透。

flask项目大名鼎鼎,不需要多做介绍。我把它称之为python服务开发的TOP2项目,另外一个就是django。这两个项目各有千秋,各自有不同的应用场景,都需要深入理解,熟练掌握。本次源码选择的版本是 1.1.2,我会采用慢读法,尽自己最大努力把它讲透。本篇是第3篇-自助餐,主要包括:


  • view 解析
  • blueprint 解析
  • 小结


view 解析



flask一个简单的监听函数如下:


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()
复制代码


如果URL多了,就需要实现多个监听函数,代码会比较乱。同时对于一个URL,在监听函数中区分http的method,进行不同的业务逻辑处理。一个函数中处理两种逻辑,也不太符合单一职责,会让代码难以维护。


这种情况下,就需要使用视图。下面是一个视图示例:


class CounterAPI(MethodView):
    def get(self):
        return session.get('counter', 0)
    def post(self):
        session['counter'] = session.get('counter', 0) + 1
        return 'OK'
app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
复制代码


CounterAPI可以把一个类的实例方法注册到一个URL上,自动将get和post方法区分开。我们一起看看View的实现,先是所有View的基类:


class View(object):
    @classmethod
    def as_view(cls, name, *class_args, **class_kwargs):
        def view(*args, **kwargs):
            self = view.view_class(*class_args, **class_kwargs)
            return self.dispatch_request(*args, **kwargs)
        ...
        view.view_class = cls
        view.__name__ = name
        ...
        return view
复制代码


as_view函数返回一个视图函数,在视图函数里可以派发和处理request。


MethodViewType是一个元类,定义了视图支持的所有HTTP方法的集合:


http_method_funcs = frozenset(
    ["get", "post", "head", "options", "delete", "put", "trace", "patch"]
)
class MethodViewType(type):
    def __init__(cls, name, bases, d):
        super(MethodViewType, cls).__init__(name, bases, d)
        if "methods" not in d:
            methods = set()
            ...
            for key in http_method_funcs:
                if hasattr(cls, key):
                    methods.add(key.upper())
            if methods:
                cls.methods = methods
复制代码


MethodView是使用MethodViewType和View创建的新类:


class MethodView(with_metaclass(MethodViewType, View)):
    def dispatch_request(self, *args, **kwargs):
        meth = getattr(self, request.method.lower(), None)
        ...
        return meth(*args, **kwargs)
复制代码


with_metaclass 是为了兼容python2的语法,可以简单的理解为继承自MethodViewType和View


dispatch_request中根据请求的http-method找到对应的方法,进行执行。


view的处理函数还可以增加装饰器,示例如下:


# 使用示例
class SecretView(View):
    methods = ['GET']
    decorators = [login_required]
class View(object):
    @classmethod
    def as_view(cls, name, *class_args, **class_kwargs):
        ...
        if cls.decorators:
            view.__name__ = name
            view.__module__ = cls.__module__
            # 包装上装饰器
            for decorator in cls.decorators:
                view = decorator(view)
        ...
        return view
# 装饰器
def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for('auth.login'))
        return view(**kwargs)
    return wrapped_view
复制代码


blueprint 解析



View相对还是比较单薄,大型的项目都会分模块进行开发,所以flask还有blueprint的概念。下面是示例项目flaskr中的 auth.py :


import functools
from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)
...
bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/register', methods=('GET', 'POST'))
def register():
    ...
@bp.route('/login', methods=('GET', 'POST'))
def login():
    ...
@bp.route('/logout')
def logout():
    ...
复制代码


这里定义了一个名称叫做auth的蓝图,里面定义了3个方法: register , login 和 logout 。蓝图在app的__init__.py中注册:


def create_app():
    app = ...
    # existing code omitted
    from . import auth
    app.register_blueprint(auth.bp)
    return app
复制代码


在flask项目中还有名为blog的蓝图,提供博客文章的增删改查方法:


bp = Blueprint("blog", __name__)
@bp.route("/")
def index():
    ...
@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
    ...
复制代码


采用这种方式,就可以很方便的分模块进行程序开发。


了解了bluerpint的使用方法后,我们再看看其实现原理。


class Blueprint(_PackageBoundObject):
    def __init__(
        self,
        name,
        import_name,
        static_folder=None,
        static_url_path=None,
        template_folder=None,
        url_prefix=None,
        subdomain=None,
        url_defaults=None,
        root_path=None,
        cli_group=_sentinel,
    ):
        _PackageBoundObject.__init__(
            self, import_name, template_folder, root_path=root_path
        )
        self.name = name
        self.url_prefix = url_prefix
        self.subdomain = subdomain
        self.static_folder = static_folder
        self.static_url_path = static_url_path
        self.deferred_functions = []
        if url_defaults is None:
            url_defaults = {}
        self.url_values_defaults = url_defaults
        self.cli_group = cli_group
复制代码


上面Blueprint的构造函数中显示:


  • 继承自_PackageBoundObject。_PackageBoundObject上一篇介绍过,主要实现本地目录的动态加载,因为蓝图也有一些模版需求,所以继承了_PackageBoundObject。
  • deferred_functions数组是蓝图的所有视图的集合
  • url_prefix,subdomain, static_folder等是蓝图模块化的功能参数


蓝图的route装饰器:


def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop("endpoint", f.__name__)
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    ...
    self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
def record(self, func):
    self.deferred_functions.append(func)
复制代码


这里主要的疑问在添加视图函数时候的lambda函数的参数 s 是什么?继续看看蓝图的注册:


# app的方法
def register_blueprint(self, blueprint, **options):
    self.blueprints[blueprint.name] = blueprint
    self._blueprint_order.append(blueprint)
    first_registration = True
    blueprint.register(self, options, first_registration)
# blueprint的方法
def register(self, app, options, first_registration=False):
    self._got_registered_once = True
    state = self.make_setup_state(app, options, first_registration)
    for deferred in self.deferred_functions:
        deferred(state)
   ...
复制代码


make_setup_stat创建BlueprintSetupState对象, 然后执行蓝图route添加到deferred_functions的方法。这个方法就是前面的lambda函数,前面的 s 就是state对象.


class BlueprintSetupState(object):
    def __init__(self, blueprint, app, options, first_registration):
        #: a reference to the current application
        self.app = app
        self.blueprint = blueprint
    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        """A helper method to register a rule (and optionally a view function)
        to the application.  The endpoint is automatically prefixed with the
        blueprint's name.
        """
        if self.url_prefix is not None:
            if rule:
                rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
            else:
                rule = self.url_prefix
        options.setdefault("subdomain", self.subdomain)
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        defaults = self.url_defaults
        if "defaults" in options:
            defaults = dict(defaults, **options.pop("defaults"))
        self.app.add_url_rule(
            rule,
            "%s.%s" % (self.blueprint.name, endpoint),
            view_func,
            defaults=defaults,
            **options
        )
复制代码


BlueprintSetupState中建立了app和blueprint的关联,并且使用app的add_url_rule方法,把blueprint的视图函数注册进入app。


小结



flask是一个 micro 框架,但是也(至少)可以支持中型项目。我们可以利用Blueprint和View功能进行模块化: View可以很好的区分URL上的http-method;Blueprint可以很好的定义子域名和URL前缀等。


目录
相关文章
|
2月前
|
人工智能 数据安全/隐私保护 异构计算
桌面版exe安装和Python命令行安装2种方法详细讲解图片去水印AI源码私有化部署Lama-Cleaner安装使用方法-优雅草卓伊凡
桌面版exe安装和Python命令行安装2种方法详细讲解图片去水印AI源码私有化部署Lama-Cleaner安装使用方法-优雅草卓伊凡
325 8
桌面版exe安装和Python命令行安装2种方法详细讲解图片去水印AI源码私有化部署Lama-Cleaner安装使用方法-优雅草卓伊凡
|
5月前
|
机器学习/深度学习 监控 算法
基于mediapipe深度学习的手势数字识别系统python源码
本内容涵盖手势识别算法的相关资料,包括:1. 算法运行效果预览(无水印完整程序);2. 软件版本与配置环境说明,提供Python运行环境安装步骤;3. 部分核心代码,完整版含中文注释及操作视频;4. 算法理论概述,详解Mediapipe框架在手势识别中的应用。Mediapipe采用模块化设计,包含Calculator Graph、Packet和Subgraph等核心组件,支持实时处理任务,广泛应用于虚拟现实、智能监控等领域。
|
2月前
|
机器学习/深度学习 数据采集 算法
基于mediapipe深度学习的运动人体姿态提取系统python源码
本内容介绍了基于Mediapipe的人体姿态提取算法。包含算法运行效果图、软件版本说明、核心代码及详细理论解析。Mediapipe通过预训练模型检测人体关键点,并利用部分亲和场(PAFs)构建姿态骨架,具有模块化架构,支持高效灵活的数据处理流程。
|
2月前
|
小程序 PHP 图形学
热门小游戏源码(Python+PHP)下载-微信小程序游戏源码Unity发实战指南​
本文详解如何结合Python、PHP与Unity开发并部署小游戏至微信小程序。涵盖技术选型、Pygame实战、PHP后端对接、Unity转换适配及性能优化,提供从原型到发布的完整指南,助力开发者快速上手并发布游戏。
|
6月前
|
SQL 数据库 开发者
Python中使用Flask-SQLAlchemy对数据库的增删改查简明示例
这样我们就对Flask-SQLAlchemy进行了一次简明扼要的旅程,阐述了如何定义模型,如何创建表,以及如何进行基本的数据库操作。希望你在阅读后能对Flask-SQLAlchemy有更深入的理解,这将为你在Python世界中从事数据库相关工作提供极大的便利。
614 77
|
4月前
|
算法 数据可视化 数据挖掘
基于EM期望最大化算法的GMM参数估计与三维数据分类系统python源码
本内容展示了基于EM算法的高斯混合模型(GMM)聚类实现,包含完整Python代码、运行效果图及理论解析。程序使用三维数据进行演示,涵盖误差计算、模型参数更新、结果可视化等关键步骤,并附有详细注释与操作视频,适合学习EM算法与GMM模型的原理及应用。
|
4月前
|
API 数据安全/隐私保护 开发者
企业微信自动加好友软件,导入手机号批量添加微信好友,python版本源码分享
代码展示了企业微信官方API的合规使用方式,包括获取access_token、查询部门列表和创建用户等功能
|
3月前
|
并行计算 算法 Java
Python3解释器深度解析与实战教程:从源码到性能优化的全路径探索
Python解释器不止CPython,还包括PyPy、MicroPython、GraalVM等,各具特色,适用于不同场景。本文深入解析Python解释器的工作原理、内存管理机制、GIL限制及其优化策略,并介绍性能调优工具链及未来发展方向,助力开发者提升Python应用性能。
218 0
|
4月前
|
机器人 API 数据安全/隐私保护
QQ机器人插件源码,自动回复聊天机器人,python源码分享
消息接收处理:通过Flask搭建HTTP服务接收go-cqhttp推送的QQ消息47 智能回复逻辑
|
7月前
|
前端开发 JavaScript 关系型数据库
基于python的租房网站-房屋出租租赁系统(python+django+vue)源码+运行
该项目是基于python/django/vue开发的房屋租赁系统/租房平台,作为本学期的课程作业作品。欢迎大家提出宝贵建议。
214 6

推荐镜像

更多