Flask之endpoint

最近在学习Flask, 其中遇到了一个错误, 发现这个问题和Flask, 路由有关系, 所以就记了下来

错误代码:

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

app = Flask(__name__)
app.secret_key = "wang"


def confirm(func):  # 负责确认用户有没有登陆的装饰器
    def inner(*args, **kwargs):
        if session.get("auth"):  # 判断用户的session中没有user
            return func(*args, **kwargs)  # 通过
        else:  # 跳转登陆页面, 并携带当前访问的url
            next_url = request.path
            return redirect(f'/login?next={next_url}')

    return inner


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


@app.route('/login', methods=["GET", "POST"])
def login():
    msg = ''
    if request.method == "POST":
        auth = request.form.get('auth')
        if auth == 'wang':  # 简单认证
            session['auth'] = auth  # 设置session
            next_url = request.args.get('next_url', "/")  # 获取用户之前访问的url, 进行跳转
            return redirect(next_url)
        else:
            msg = "口令错误"
    return render_template("login.html", msg=msg)


@app.route('/shopping')
@confirm
def shopping():
    return "购物"


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

报错:

诡异的是, 我不启动flask, 只是解释一遍, 也会报错

报错分析

分析报错提示

根据报错的提示, 说我的代码存在重复的函数, 然后就开始检查我的函数, 发现函数名并没有重复, 难道就这样排除函数名的嫌疑吗? NONONO

可能是我对装饰器的理解还不够, 找了好半天才发现这个问题, 原来是装饰器的原因, 为什么呢?

为什么说是因为装饰器, 才会出现函数覆盖的问题?

装饰器代码

def test(func):  # 装饰器
    """
    test
    :param func: 其实就是要装饰的函数
    :return:
    """

    def inner(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        return end - start

    return inner


def outer(a, b):  # 被装饰的函数
    for n in range(a):
        for j in range(b):
            a = n + j


outer = test(outer)  # 这里因为使用语法糖, 这种方式更能表示出问题
# 在这一步可以说对outer进行了重新的赋值,
# 现在outer就等于test这个函数的返回值, 并且将原本的outer传了进去
# test函数的返回值是一个inner
# 在inner函数中就包括了原本的outer, 并且这个outer在inner函数中是加了括号的
# 也就是说, 当inner被调用的时候, 原本的outer也会被调用
# 刚刚说test函数返回的是inner函数
# 当outer = test(outer)执行完之后, 新的outer就等于inner了
# 到这只需要知道现在的outer一样不是原来的outer了, 而是指向了inner, 在inner内部调用原来的outer


print(outer(100000, 200))  # 这是调用函数, 不能改变这个调用方式

再来看flask中app.route中的源码

 flask使用装饰器来绑定一个url和视图的关系, 带着遇到的问题来看看源码中做了些什么

@app.route('/shopping')     ①
@confirm
def shopping():
    return "购物"

def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop('endpoint', None)  # ② 从参数中弹出endpoint, 没有的话就是None
        self.add_url_rule(rule, endpoint, f, **options) #
        return f
        
@setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None,
                     provide_automatic_options=None, **options):
                     
        if endpoint is None:  # ④ endpoint如果为None
            endpoint = _endpoint_from_view_func(view_func)  # ⑤ 将试图视图函数传了进去, 返回视图函数的__name__

def _endpoint_from_view_func(view_func):
    assert view_func is not None, 'expected view func if endpoint is not provided.'
    ~~~~ 其实执行的就是 inner.__name__, 因为此时的shopping, 已经不是原来的shopping, 而是装饰器内部返回的函数
    return view_func.__name__  # ⑥ 因为没有定义endpoint, 所以在返回视图函数的名字

猜你喜欢

转载自www.cnblogs.com/594504110python/p/10131950.html
今日推荐