Python学习笔记12:函数修饰符的应用

Python学习笔记12:函数修饰符的应用

上一篇笔记Python学习笔记11:函数修饰符介绍了如何构建自己的函数修饰符,这篇笔记通过使用函数修饰符改进web应用来演示如何在实际使用中运用。

用函数修饰符改进web应用

添加注册和登录功能

我们先给web应用添加一个很常见的功能:注册和登录。

先新建一个用户表:

CREATE TABLE `myweb`.`user`( `id` INT NOT NULL AUTO_INCREMENT COMMENT '用户id', `name` VARCHAR(20) NOT NULL COMMENT '用户名', `password` VARCHAR(30) NOT NULL COMMENT '密码', `ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ); 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6jHaqHLn-1616572175066)(https://i.loli.net/2021/03/24/y4iKscp6vdIMzuJ.png)]

先来添加一个注册页面:

@web.route("/showRegist", methods=["GET"])
def showRegist():'''用户注册页面'''return render_template("regist.html")

相应的模板文件regist.html的代码这里就不展示了,本文末尾会附上所有工程文件,感兴趣的自行下载。

再加入一个页面用于表单提交注册信息:

@web.route("/doRegist", methods=["POST"])
def doRegist():'''注册用户'''name = request.form['name']password = request.form['password']redirectUrl = ''with MyDB2() as cursor:_SQL = '''SELECT * FROM user
WHERE name=%s LIMIT 1'''cursor.execute(_SQL, (name,))users = cursor.fetchall()if len(users) > 0:# 已存在同名用户redirectUrl = "/showError/already has same user/1"# redirect("/showError/already has same user/1")else:# 注册用户_SQL = '''INSERT INTO `myweb`.`user` (`name`, `password`) VALUES (%s, %s); '''params = (name, password)cursor.execute(_SQL, params)# redirect('/')redirectUrl = '/'if redirectUrl != '':return redirect(redirectUrl)

我们在注册逻辑中加入了重名检查,所以需要再建立一个错误信息显示页面:

@web.route("/showError//", methods=["GET"])
def showError(msg, type):'''错误显示页面'''if type == 1:url = '/showRegist'else:url = '/showRegist'return render_template('error.html', msg=msg, url=url)

错误信息显示页面需要接收错误信息等参数,关于flask如何接收url参数,可以阅读这里

现在可以试试新加入的注册页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KeNE0crU-1616572175070)(https://i.loli.net/2021/03/24/FwNZyisrmpDKPeY.png)]

点击后如果没有重名,就会注册用户并跳转到其它页面,我们再看数据库:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzeySymm-1616572175073)(https://i.loli.net/2021/03/24/6vVqaPY4Nwdc5yK.png)]

用户添加成功。

这里的演示相当粗糙(我并不是指页面中英文混杂),我是说这里有很大的安全问题:

  • 密码输入框没有用*掩盖用户输入的密码。
  • 缺少重复输入密码验证。
  • 密码以明文方式存储在数据库。

这些在实际使用中是不可能接受的,这里只用于演示。

我们再用类似的方式建立用户登录页面:

@web.route("/login", methods=["GET"])
def login():'''登录页面'''return render_template('login.html')@web.route('/doLogin', methods=["POST"])
def doLogin():'''执行登录逻辑'''name = request.form['name']password = request.form['password']if checkUserPassword(name,password)==False:return redirect('/showError/password is error/2')return redirect('/')def checkUserPassword(user, password):with MyDB2() as cursor:_SQL = '''SELECT * FROM USER
WHERE NAME=%s LIMIT 1;'''params = (user,)cursor.execute(_SQL, params)users = cursor.fetchall()if len(users) == 0:return FalserealPassword = users[0][2]if password != realPassword:return Falsereturn True

现在我们的登录注册逻辑都有了,但是仅仅加上了两个功能而已,并不能实用化,因为没有使用session进行状态保持。

使用session进行状态保持

在flask框架中使用session很简单:

from flask import Flask, render_template, request, redirect, session
from mydb2 import MyDB2
web = Flask(__name__)
web.secret_key="sfdfjk@.sfdsf"

只需要两步:

  1. 从flask模块导入session。
  2. 给flask实例设置一个用于给session加密的secret_key

我们先要在登录逻辑中写入session:

@web.route('/doLogin', methods=["POST"])
def doLogin():'''执行登录逻辑'''name = request.form['name']password = request.form['password']checkResult = checkUserPassword(name, password)if not checkUserPassword:return redirect('/showError/password is error/2')# 登录成功,写入sessionsession['uid'] = checkResult['uid']session['name'] = checkResult['name']return redirect('/')

然后写一个函数根据session来判断是否已经登录:

def isLogged():'''判断当前是否登录状态'''if not 'uid' in session:return Falseuid = int(session['uid'])if uid<=0:return Falsereturn True

OK,现在我们只需要给登录后才能看的页面加上登录检查的逻辑:

@web.route('/log')
def showLog() -> 'html':"""展示日志页面"""if not isLogged():return redirect('/login')logList = getLog()return render_template("log.html", the_title="服务器日志", log_list=logList)

好像挺简单的,我们这里只是一个日志页需要访问控制,但如果有很多页面呢?难道要一个一个复制粘贴if语句吗?

当然不是,该是我们的函数修饰符闪亮登场的时候了。

使用函数修饰符改善既有代码

我们新建一个login_check.py用于编写函数修饰符:

from flask import session, redirect
from functools import wrapsdef isLogged():'''判断当前是否登录状态'''if not 'uid' in session:return Falseuid = int(session['uid'])if uid <= 0:return Falsereturn Truedef loginCheck(webRoute):'''函数修饰符,用于给webroute增加登录检查'''@wraps(webRoute)def loginCheckWebRoute(*params, **kvParams):if not isLogged():return redirect('/login')return webRoute(*params, **kvParams)return loginCheckWebRoute

这并不难,我们的函数修饰符只是经过三个步骤:

  1. 接收一个web应用的页面路由函数。
  2. 创建一个内部函数,在内部函数中实现先检查登录状态,如果已登录,就执行路由函数并返回其结果。
  3. 将内部函数作为返回值返回。

好了,现在我们可以在我们的主文件index.py中应用这个函数修饰符了。

@web.route('/log')
@loginCheck
def showLog() -> 'html':"""展示日志页面"""logList = getLog()return render_template("log.html", the_title="服务器日志", log_list=logList)

在删除不必要的代码后,只需要在路由函数前面加入@loginCheck就可以实现在进入页面逻辑前先检查登录状态。

当然,你也可以照例给其它想要访问控制的页面加入登录检查。

现在我们应该对函数修饰符的优点有所感悟:

  • 减少了重复代码的使用。

    当然,你也可以用函数包装重复代码后再调用,但无疑函数修饰符的写法更简洁

  • 在没有改变原有业务代码的情况下增强了代码行为,这可以让你的代码更简洁明了,将业务代码和基础性的功能代码剥离。

    如果你有商业开发经验,你会对这一点深有体会。

好了,我们现在已经讲完了如何在实际工作中使用函数修饰符。我们现在可以讨论一些其它的关于函数修饰符的主题。

在这里附上目前web应用的全部工程代码:

链接:https://pan.baidu.com/s/1fm18rgWKYy_HbN9ugtREiw
提取码:nqrz
复制这段内容后打开百度网盘手机App,操作更方便哦

函数修饰符与设计模式

Python学习笔记11:函数修饰符的末尾我说过,函数修饰符这种称呼很别扭,老实说,我第一次看到的时候以为@这个符号是函数修饰符,后来才知道它只是用来标记函数修饰符。

在我看来,其实称呼为函数修饰器更为妥当,对设计模式有所了解的朋友应该知道,设计模式中有种叫做修饰器或者说装饰器模式。其核心思想就是通过一系列技术对已有类进行封装,以实现增强其功能的目的。而这和Python中的函数修饰器的用途别无二致。

在理解设计模式的时候,我们要时刻保持这样一种认识:设计模式只是一种思想,是为了解决某一类相似问题总结出的解决方案。而且各种设计模式之前只有明显的倾向,而并没有明显的分界线

比如装饰器和适配器,当初我阅读设计模式的时候相当费解,傻傻分不清。后来我偶然注意到电源适配器,才茅塞顿开。

适配器,顾名思义,其侧重点在于适配不同的部件,比如笔记本的电源适配器,如果没有,你直插220V高压电插座,那你笔记本立即阵亡,这时候,就需要一个适配器来对电源插座和笔记本做适配,充当之间的桥梁,让你笔记本安全使用到低压直流电。

而装饰器的侧重点在增强已有物品的功能,比如前文举的例子,给坦克加装反应装甲。

当然,你完全可以通过“魔改”装饰器来起到适配器的作用,比如我找一个某电工大神,给笔记本电脑焊装一个扩展,可以直接接高压直流电的那种。也可以实现目的,但是其可扩展性和灵活性就低很多了。

所以你看,重要的并不是其实现细节和能否互相替代,而是在合适的场景选取合适的设计模式

现在我们说回来,这里讲的函数修饰符和Python学习笔记10:上下文协议中说的上下文协议,是两种设计模式,他们的侧重点不同,上下文协议更倾向于让某段代码实现自动的上下文切换。

而如同之前我们讨论的,我们完全可以用函数修饰符来尝试实现上下文协议,虽然这种“魔改”行为没有多少实际价值,但对我们理解Python本身还是一个不错的体验,更何况比较有趣。

from functools import wraps
def wrapContent(contentFunc):@wraps(contentFunc)def wrapContentFunc(*params,**vkParams):print("this is a before action")beforeReturn = [1,2,3]vkParams['beforeReturn'] = beforeReturncontentFunc(*params,**vkParams)print("this is a after action")return wrapContentFunc@wrapContent
def contentFunc(beforeReturn):print("this is a content")print(beforeReturn)contentFunc()

输出

this is a before action
this is a content
[1, 2, 3]
this is a after action

这里最难的部分是上下文切换的时候会传给业务代码段一个句柄,这里我通过在内部函数中给可变参数vkParams追加一个参数的方式实现句柄传递。

好了,关于函数修饰符的讨论告一段落,感谢阅读。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部