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 相关文章推荐
Flask框架学习笔记(一)安装篇(windows安装与centos安装)
Jun 25 Python
基于python脚本实现软件的注册功能(机器码+注册码机制)
Oct 09 Python
pygame加载中文名mp3文件出现error
Mar 31 Python
使用Python的turtle模块画图的方法
Nov 15 Python
python 获取指定文件夹下所有文件名称并写入列表的实例
Apr 23 Python
python logging重复记录日志问题的解决方法
Jul 12 Python
python实现顺时针打印矩阵
Mar 02 Python
python Pandas库基础分析之时间序列的处理详解
Jul 13 Python
tensorflow获取预训练模型某层参数并赋值到当前网络指定层方式
Jan 24 Python
python爬虫之selenium库的安装及使用教程
May 23 Python
教你使用一行Python代码玩遍童年的小游戏
Aug 23 Python
Python Django模型详解
Oct 05 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中mysql_connect与mysql_pconncet的区别详解
2013/05/15 PHP
PHP实现的简单网络硬盘
2015/07/29 PHP
PHP 观察者模式深入理解与应用分析
2019/09/25 PHP
PHP网页缓存技术优点及代码实例
2020/07/29 PHP
删除select中所有option选项jquery代码
2013/08/12 Javascript
JQuery弹出层示例可自定义
2014/05/19 Javascript
Jquery中offset()和position()的区别分析
2015/02/05 Javascript
JavaScript动态添加事件之事件委托
2016/07/12 Javascript
Vue.js 60分钟快速入门教程
2017/03/28 Javascript
for循环 + setTimeout 结合一些示例(前端面试题)
2017/08/30 Javascript
axios post提交formdata的实例
2018/03/16 Javascript
vue element-ui读取pdf文件的方法
2019/11/26 Javascript
vue实现一个6个输入框的验证码输入组件功能的实例代码
2020/06/29 Javascript
[01:08]2014DOTA2展望TI 剑指西雅图LGD战队专访
2014/06/30 DOTA
[01:32:10]NAVI vs VG Supermajor 败者组 BO3 第一场 6.5
2018/06/06 DOTA
[54:58]完美世界DOTA2联赛PWL S2 LBZS vs Rebirth 第一场 11.25
2020/11/25 DOTA
在Python中处理字符串之isdigit()方法的使用
2015/05/18 Python
Python错误: SyntaxError: Non-ASCII character解决办法
2017/06/08 Python
Python实现解析Bit Torrent种子文件内容的方法
2017/08/29 Python
Python WSGI的深入理解
2018/08/01 Python
Python找出列表中出现次数最多的元素三种方式
2020/02/24 Python
Win10下用Anaconda安装TensorFlow(图文教程)
2020/06/18 Python
css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局
2014/10/10 HTML / CSS
Lee牛仔裤澳大利亚官网:美国著名牛仔裤品牌
2017/09/02 全球购物
日本索尼音乐商店:Sony Music Shop
2018/07/17 全球购物
美国最大的在线水培用品商店:GrowersHouse.com
2018/08/14 全球购物
法国发饰品牌:Alexandre De Paris
2018/12/04 全球购物
Aosom西班牙:家具在线商店
2020/06/11 全球购物
C#如何允许一个类被继承但是避免这个类的方法被重载?
2015/02/24 面试题
社会实践自我鉴定
2013/11/07 职场文书
幼儿园优秀班主任事迹材料
2014/05/14 职场文书
企业员工爱岗敬业演讲稿
2014/08/26 职场文书
产品委托授权书范本
2014/09/16 职场文书
教师节随笔
2015/08/15 职场文书
vue使用localStorage持久性存储实现评论列表
2022/04/14 Vue.js
使用 CSS 构建强大且酷炫的粒子动画效果
2022/08/14 HTML / CSS