python–装饰器
Flask已经学习很多基础知识,现在有一个问题:现在有一个 Flask 程序其中有3个路由和视图函数,如下:
from flask import Flask
app = Flask(__name__) # type:Flask
@app.route("/login")
def login():
return "Login"
@app.route("/index")
def index():
return "Index"
@app.route("/home")
def home():
return "Login"
if __name__ == '__main__':
app.run("0.0.0.0", 5000)
如果登陆了,就可以访问 index 和 home 页面,如果没登录就跳转到 login 登录。要怎么解决呢?用 session 除了 Login 函数之外的所有函数里面全校验 session 是否登录,这样太麻烦了,现在只有3个函数,如果成百上千个函数就需要在所有函数中添加session校验;用装饰器,装饰器是一个很好的方案。
Decorator(修饰器)就是Python内部提供的一个语法便利,用于更加快捷、方法、直观地实现修饰器模式。修饰器模式会使用得代码结构比较复杂,而decorator则不存在这个问题。
一、装饰器模式
**Decorator(修饰器)**就是Python内部提供的一个语法便利,用于更加快捷、方法、直观地实现修饰器模式。修饰器模式会使用得代码结构比较复杂,而decorator则不存在这个问题。
在Python中,万物皆对象。(Everything in Python is an object.)
当你定义一个函数的时候,你就得到了一个函数对象,这个对象的引用就是函数名。通过函数名,你可以做很多事情:
# 定义一个加法函数
def add(a, b):
return a + b
# 调用函数
sum1 = add(1, 2)
# 把函数赋值给其它变量
sum_of_two_num = add
# 通过其它变量调用add
sum2 = sum_of_two_num(2, 3)
print(sum1, sum2) # 打印 3 5
######################################和谐的分割线###################################
def double(x):
return 2 * x
# 一个数字列表
nums = [1, 2, 3, 4, 5]
# 将每个数自乘以2
# map(func, iter)就是对iter中的每个元素e调用 func(e)
# 下面的代码相当于 double_nums = list(map(lambda x: 2 * x, nums))
dubled_nums = list(map(double, nums))
print(dubled_nums) # 打印 [2, 4, 6, 8, 10]
######################################和谐的分割线###################################
# 根据lan返回不同版本的greet函数
def getGreet(lan):
if lan == "En":
def greet(name):
return "Hello " + name
elif lan == "Es":
#lambda是一种不需要名字(即标识符)、由一个单独表达式成的匿名内联函数,表达式会在调用时被求值。
return lambda name: "Hola " + name
else:
return lambda name: "Ciao " + name
greet = getGreet("Es") # 获取西语版本的greet函数
# Say "Hello World" in Spanish
print(greet("Mondo")) # 打印 Hola Mondo
**若要求实现整数的四则运行函数,并且保证每个函数的参数都为整数,如果有至少一个参数不为整数,则直接返回0。**四则运行4个函数在参数合法性检测方面的行为是一样的,所以可以通过定义一个公共的参数检测函数来实现。
def arg_check(x):
return type(x) == int
def add(a, b):
if not arg_check(a) or not arg_check(b):
return 0
return a + b
这是常见的方法,就是在实现四则运算前做一个检测,但所有四则运算方法内都需要增加数字类型检测会过于繁琐。
一个优化的思路,就是利用**“修饰器模式”**。修饰器模式就是在不改变现有功能的代码的基础上,为功能增加额外的行为。
def arg_check(x):
return type(x) == int
def add(a, b):
return a + b
#装饰函数
def checker(func):
def new_func(a, b):
# 额外的功能
if not arg_check(a) or not arg_check(b):
return 0
# 原来的功能
return func(a, b)
# 返回一个新的函数,这个函数增加了额外的功能
# 并保留了原函数的功能
return new_func
# 通过调用checker来为每个函数增加额外的行为
# 即对函数进行修饰
add = checker(add)
通过checker函数,用返回值来替代原函数,这就是装饰器模式。
二、decorator
通过装饰器模式就大概已经知道decorator怎么实现了,装饰器就是python通过装饰器模式的原理提供的一种语法便利。
def arg_check(x):
return type(x) == int
def checker(func):
def new_func(a, b):
# 额外的功能
if not arg_check(a) or not arg_check(b):
return 0
# 原来的功能
return func(a, b)
# 返回一个新的函数,这个函数增加了额外的功能
# 并保留了原函数的功能
return new_func
@checker
def add(a, b):
return a + b
这就是修饰器的使用方法了,可以看出:
def xx(a, b):
# blabla
xx = checker(xx)
# 等价于
@checker
def xx(a, b):
# blabla
三、带额外参数的Decorator
def arg_check(x):
return type(x) == int
def checker(log="arithmatic.log"):
def real_dec(func):
def new_func(a, b):
# 参数检测
if not arg_check(a) or not arg_check(b):
return 0
# 打印日志
fout = open(log, mode="w")
fout.write("called: %s(%d, %d)\n".format(func.__name__, a, b))
fout.close()
# 原来的功能
return func(a, b)
return new_func
return real_dec
@checker("add.log")
def add(a, b):
return a + b
通过这个方式,add每次被调用,都会分别向add.log写入一条日志;但无论我们是否提供参数,checker的括号都不可省略。
四、flask中特殊的装饰器
from flask import Flask
from flask import request
from flask import redirect
from flask import session
app = Flask(__name__) # type:Flask
app.secret_key = "DragonFire"
@app.before_request
def is_login():
if request.path == "/login":
return None
if not session.get("user"):
return redirect("/login")
@app.route("/login")
def login():
return "Login"
@app.route("/index")
def index():
return "Index"
@app.route("/home")
def home():
return "Login"
if __name__ == '__main__':
#启动的是 flask自带的服务器,也就是Werkzeug这个做WSGI 容器
app.run("0.0.0.0", 5000)
#启动的是wsgiref这个模块的 make_server做 WSGI容器。
#make_server("0.0.0.0", 5000, app)
@app.before_request 也是一个装饰器,他所装饰的函数,都会在请求进入视图函数之前执行。
request.path 是来读取当前的url地址,如果是 /login 就允许直接通过 return None ,理解成通过放行。校验session中是否有user ,如果没有的话,证明没有登录,所以毫不留情的 redirect(“/login”) 跳转登录页面。
还有一个要提的 @app.before_first_request 它与 @app.before_request 极为相似或者说是一模一样,只不过它只会被执行一次。
使用**@app.before_request**装饰器可以很好的解决文章开头提出的问题。
@app.after_request :在响应(response)之前做出响应
@app.errorhandler() :重定义错误页面返回信息
比如:当404时则会执行下面的视图函数
@app.errorhandler(404)
def error404(error_info):
return 三剑客+小儿子