Python的Flask框架中@app.route的用法教程


Posted in Python onMarch 31, 2015

在我上一篇文章,我搭了一个框架,模拟了Flask网站上“@app.route(‘/')”第一条例子的行为。

如果你错过了那篇“这不是魔法”,请点击这里。

在这篇文章中,我们打算稍微调高点难度,为我们的URL加入可变参数的能力,在本文的最后,我们将支持下述代码段所期望达到的行为。

app = Flask(__name__)
 
@app.route("/hello/<username>")
def hello_user(username):
  return "Hello {}!".format(username)

这样下面的路径实例(path):
/hello/ains
将会匹配上面的路径,给我们的输出为
Hello ains!

以正则的形式表达我们的路径。

现在我们将允许我们的URL动态变化,我们不再能够将用先前使用“@app.route()”注册的路径直接与路径实例比较。

我们将用什么替代?我们需要用上正则表达式,这样我们就可以将路径作为一种模式进行匹配,而不和一条固定的字符串比较了。

我不打算在本文展开讨论正则表达式的细节,不过如果你需要一份复习资料,可以点击这个网站。

那么,我们的第一步是将我们的路径转化成正则表达式模式,这样我们就能在输入路径实例时进行匹配。我们也将使用这个正则表达式提取我们感兴趣的变量。

那么,匹配路径”/hello/”的正则表达式该长啥样呢?

嗯一个简单的正则表达式譬如“^/hello/(.+)$”将是个好的开始,让我们一起看看它和代码是怎么一起工作的:

import re
 
route_regex = re.compile(r"^/hello/(.+)$")
match = route_regex.match("/hello/ains")
 
print match.groups()

将会输出:
('ains',)

不错,不过,理想情况是我们想要维护我们已经匹配上的第一组链接,并且从路径“/hello/”识别出“username”。

命名捕获组

幸运的是,正则表达式也支持命名捕获组,允许我们给匹配组分配一个名字,我们能在读取我们的匹配之后找回它。

我们可以使用下述符号,给出第一个例子识别“username”的捕获组。
 

/hello/(<?P<username>.+)"

然后我们可以对我们的正则表达式使用groupdict()方法,将所有捕获组当作一个字典,组的名字对应匹配上的值。

那么我们给出下述代码:
 

route_regex = re.compile(r'^/hello/(?P<username>.+)$')
match = route_regex.match("/hello/ains")
 
print match.groupdict()

将为我们输出以下字典:
{'username': 'ains'}

现在,有了我们所需要的正则表达式的格式,以及如何使用它们去匹配输入的URLs的知识,最后剩下的是写一个方法,将我们声明的路径转换成它们等价的正则表达式模式。

要做这个我们将使用另一个正则表达式(接下来将全是正则表达式),为了让我们路径中的变量转换成正则表示式模式,那这里作为示范我们将将“”转换成“(?P.+)”。

听起来太简单了!我们将可以只用一行新代码实现它。

def build_route_pattern(route):
  route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route)
  return re.compile("^{}$".format(route_regex))
 
print build_route_pattern('/hello/<username>')

这里我们用一个正则表达式代表所有出现的模式(一个包含在尖括号中的字符串),与它的正则表达式命名组等价。

re.sub的第一个参数 我们将我们的模式放进括号,目的是把它分配到第一个匹配组。在我们的第二个参数,我们可以使用第一匹配组的内容,方法是写1(2将是第二匹配组的内容,以此类推…….)

那么最后,输入模式
 

/hello/<username>

将给我们正则表达式:
 

^/hello/(?P<username>.+)$

推陈出新

让我们扫一眼上次我们写的简单NotFlask类。
 

class NotFlask():
  def __init__(self):
    self.routes = {}
 
  def route(self, route_str):
    def decorator(f):
      self.routes[route_str] = f
      return f
 
    return decorator
 
  def serve(self, path):
    view_function = self.routes.get(path)
    if view_function:
      return view_function()
    else:
      raise ValueError('Route "{}"" has not been registered'.format(path))
 
app = NotFlask()
 
@app.route("/")
def hello():
  return "Hello World!"

现在我们有一个新的改进方法用来匹配输入的路径,我们打算移除我们上一版实现时用到的原生字典。

让我们从改造我们的函数着手,以便于添加路径,这样我们就可以用(pattern, view_function)对列表代替字典保存我们的路径。

这意味着当一个程序员使用@app.route()装饰一个函数,我们将要尝试将他们的路径编译变成一个正则表达式,然后存储它,属于一个在我们新的路径列表里的装饰函数。

让我们看看实现代码:

class NotFlask():
  def __init__(self):
    self.routes = []
 
  # Here's our build_route_pattern we made earlier
  @staticmethod
  def build_route_pattern(route):
    route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route)
    return re.compile("^{}$".format(route_regex))
 
  def route(self, route_str):
    def decorator(f):
      # Instead of inserting into a dictionary,
      # We'll append the tuple to our route list
      route_pattern = self.build_route_pattern(route_str)
      self.routes.append((route_pattern, f))
 
      return f
 
    return decorator

我们也打算需要一个get_route_match方法,给它一个路径实例,将会尝试并找到一个匹配的view_function,或者返回None如果一个也找不到的话。

然而,如果找了到匹配的话,除了view_function之外,我们还需要返回一个东西,那就是我们包含之前捕获匹配组的字典,我们需要它来为视图函数传递正确的参数。

好了我们的get_route_match大概就长这样:

def get_route_match(path):
  for route_pattern, view_function in self.routes:
    m = route_pattern.match(path)
    if m:
      return m.groupdict(), view_function
 
  return None

现在我们快要完成了,最后一步将是找出调用view_function的方法,使用来自正则表达式匹配组字典的正确参数。

调用一个函数的若干种方法

让我们回顾一下不同的方法调用一个python的函数。

比如像这样:

def hello_user(username):
  return "Hello {}!".format(username)

最简单的(也许正是你所熟知的)办法是使用正则参数,在这里参数的顺序匹配我们定义的那些函数的顺序。

>>> hello_user("ains")
Hello ains!

另一种方法调用一个函数是使用关键词参数。关键词参数可以通过任何顺序指定,适合有许多可选参数的函数。

>>> hello_user(username="ains")
Hello ains!

在Python中最后一种调用一个函数的方法是使用关键词参数字典,字典中的关键词对应参数名称。我们告诉Python解包一个字典,并通过使用两个星号“**”来把它当作函数的关键词参数。 下面的代码段与上面的代码段完全一样,现在我们使用字典参数,我们可以在运行时动态创建它。

>>> kwargs = {"username": "ains"}
>>> hello_user(**kwargs)
Hello ains!

好了,还记得上面的groupdict()方法?就是那个同样的在正则表达式完成匹配后返回{“username”: “ains”}的家伙?那么现在我们了解了kwargs,我们能很容易向我们的view_function传递字典匹配,完成NotFlask!

那么让我们把这些都塞进我们最终的类中。
 

class NotFlask():
  def __init__(self):
    self.routes = []
 
  @staticmethod
  def build_route_pattern(route):
    route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route)
    return re.compile("^{}$".format(route_regex))
 
  def route(self, route_str):
    def decorator(f):
      route_pattern = self.build_route_pattern(route_str)
      self.routes.append((route_pattern, f))
 
      return f
 
    return decorator
 
  def get_route_match(self, path):
    for route_pattern, view_function in self.routes:
      m = route_pattern.match(path)
      if m:
        return m.groupdict(), view_function
 
    return None
 
  def serve(self, path):
    route_match = self.get_route_match(path)
    if route_match:
      kwargs, view_function = route_match
      return view_function(**kwargs)
    else:
      raise ValueError('Route "{}"" has not been registered'.format(path))

接下来,就是见证奇迹的时刻,请看下面代码段:

app = NotFlask()
 
@app.route("/hello/")
def hello_user(username):
return "Hello {}!".format(username)
 
print app.serve("/hello/ains")

我们将得到输出:

Hello ains!

Python 相关文章推荐
django2 快速安装指南分享
Jan 05 Python
python中实现将多个print输出合成一个数组
Apr 19 Python
如何在python字符串中输入纯粹的{}
Aug 22 Python
Python 3.3实现计算两个日期间隔秒数/天数的方法示例
Jan 07 Python
python爬取cnvd漏洞库信息的实例
Feb 14 Python
利用Python实现手机短信监控通知的方法
Jul 22 Python
Python3 解决读取中文文件txt编码的问题
Dec 20 Python
Python原始套接字编程实例解析
Jan 29 Python
浅谈pycharm导入pandas包遇到的问题及解决
Jun 01 Python
Python如何定义有可选参数的元类
Jul 31 Python
Python三维绘图之Matplotlib库的使用方法
Sep 20 Python
pycharm 实现复制一行的快捷键
Jan 15 Python
使用Python的Flask框架实现视频的流媒体传输
Mar 31 #Python
在Python3中初学者应会的一些基本的提升效率的小技巧
Mar 31 #Python
使用IronPython把Python脚本集成到.NET程序中的教程
Mar 31 #Python
提升Python程序运行效率的6个方法
Mar 31 #Python
用Python从零实现贝叶斯分类器的机器学习的教程
Mar 31 #Python
利用Python的Flask框架来构建一个简单的数字商品支付解决方案
Mar 31 #Python
用Python进行基础的函数式编程的教程
Mar 31 #Python
You might like
PHPMailer邮件发送的实现代码
2013/05/04 PHP
解析csv数据导入mysql的方法
2013/07/01 PHP
php检查字符串中是否有外链的方法
2015/07/29 PHP
如何解决phpmyadmin导入数据库文件最大限制2048KB
2015/10/09 PHP
如何让easyui gridview 宽度自适应窗口改变及fitColumns应用
2013/01/25 Javascript
SWFObject基本用法实例分析
2015/07/20 Javascript
纯javascript判断查询日期是否为有效日期
2015/08/24 Javascript
ECMA5数组的新增方法有哪些及forEach()模仿实现
2015/11/03 Javascript
全面解析Bootstrap布局组件应用
2016/02/22 Javascript
React实现双向绑定示例代码
2016/09/19 Javascript
javascript中活灵活现的Array对象详解
2016/11/30 Javascript
jQuery快速高效制作网页交互特效
2017/02/24 Javascript
vue2.0安装style/css loader的方法
2018/03/14 Javascript
p5.js入门教程之键盘交互
2018/03/19 Javascript
vue微信分享出来的链接点开是首页问题的解决方法
2018/11/28 Javascript
vue 框架下自定义滚动条(easyscroll)实现方法
2019/08/29 Javascript
ES5 模拟 ES6 的 Symbol 实现私有成员功能示例
2020/05/06 Javascript
javascript中导出与导入实现模块化管理教程
2020/12/03 Javascript
Python字符串中查找子串小技巧
2015/04/10 Python
Python爬取三国演义的实现方法
2016/09/12 Python
人工神经网络算法知识点总结
2019/06/11 Python
python全栈知识点总结
2019/07/01 Python
关于解决iframe标签嵌套问题的解决方法
2020/03/04 HTML / CSS
html5中使用hotcss.js实现手机端自适配的方法
2020/04/23 HTML / CSS
Otticanet英国:最顶尖的世界名牌眼镜, 能得到打折季的价格
2019/02/10 全球购物
哥德堡通行证:Gothenburg Pass
2019/12/09 全球购物
如何开启linux的ssh服务
2013/06/03 面试题
库房主管岗位职责
2013/12/31 职场文书
让生命充满爱演讲稿
2014/05/10 职场文书
安全口号大全
2014/06/21 职场文书
水知道答案观后感
2015/06/08 职场文书
行为习惯主题班会
2015/08/14 职场文书
2016年春季运动会通讯稿
2015/11/25 职场文书
小学体育课教学反思
2016/02/16 职场文书
从零开始在Centos7上部署SpringBoot项目
2022/04/07 Servers
Win11运行cmd提示“请求的操作需要提升”的两种解决方法
2022/07/07 数码科技