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 相关文章推荐
用实例说明python的*args和**kwargs用法
Nov 01 Python
python微信跳一跳游戏辅助代码解析
Jan 29 Python
Python操作Sql Server 2008数据库的方法详解
May 17 Python
对python的输出和输出格式详解
Dec 08 Python
python样条插值的实现代码
Dec 17 Python
Python和Java的语法对比分析语法简洁上python的确完美胜出
May 10 Python
pyqt5 使用label控件实时显示时间的实例
Jun 14 Python
python交易记录链的实现过程详解
Jul 03 Python
python二进制读写及特殊码同步实现详解
Oct 11 Python
对python中assert、isinstance的用法详解
Nov 27 Python
python3 中使用urllib问题以及urllib详解
Aug 03 Python
python使用布隆过滤器的实现示例
Aug 20 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
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
浅析php header 跳转
2013/06/17 PHP
PHP实现Javascript中的escape及unescape函数代码分享
2015/02/10 PHP
深入浅析Yii admin的权限控制
2016/08/31 PHP
jquery一句话全选/取消全选
2011/03/01 Javascript
ie 7/8不支持trim的属性的解决方案
2014/05/23 Javascript
javascript实现验证IP地址等相关信息代码
2015/05/10 Javascript
JavaScript制作淘宝星级评分效果的思路
2020/06/23 Javascript
jQuery与JavaScript节点创建方法的对比
2016/11/18 Javascript
jQuery实现根据生日计算年龄 星座 生肖
2016/11/23 Javascript
javascript垃圾收集机制的原理分析
2016/12/08 Javascript
angular directive的简单使用总结
2017/05/24 Javascript
深入理解Webpack 中路径的配置
2017/06/17 Javascript
Easyui在treegrid添加控件的实现方法
2017/06/23 Javascript
JS判断微信扫码的方法
2017/08/07 Javascript
基于JavaScript实现带数据验证和复选框的表单提交
2017/08/23 Javascript
es6 filter() 数组过滤方法总结
2019/04/03 Javascript
Js视频播放器插件Video.js使用方法详解
2020/02/04 Javascript
微信小程序实现简单的select下拉框
2020/11/23 Javascript
[00:31]DOTA2上海特级锦标赛 Fnatic战队宣传片
2016/03/04 DOTA
Python温度转换实例分析
2018/01/17 Python
python opencv之分水岭算法示例
2018/02/24 Python
python 字典中文key处理,读取,比较方法
2018/07/06 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
2019/03/14 Python
python使用socket 先读取长度,在读取报文内容示例
2019/09/26 Python
Python爬虫代理池搭建的方法步骤
2020/09/28 Python
详解解决jupyter不能使用pytorch的问题
2021/02/18 Python
学校门卫工作职责
2013/12/07 职场文书
自我评价如何写好?
2014/01/05 职场文书
信息服务专业毕业生求职信
2014/03/02 职场文书
大学活动总结格式
2014/04/29 职场文书
2014年母亲节演讲稿范文
2014/05/07 职场文书
大学新生入学教育方案
2014/05/16 职场文书
4s店活动策划方案
2014/08/25 职场文书
公司合作协议范文
2014/10/01 职场文书
教你用python实现一个无界面的小型图书管理系统
2021/05/21 Python