flask最近终于发布了它的1.0版本更新,从项目开源到最近的1.0版本flask已经走过了8个年头。

# app.py
from flask import Flask
app = Flask(__name__)@app.route("/")
def hello():return "Hello World!"if __name__ == "__main__":app.run()
复制代码

运行python app.py,打开浏览器访问http://localhost:5000/就可以看到页面输出了Hello World!

flask的诞生于2010年的愚人节,本来它只是作者无意间写的一个小玩具,没想到它却悄悄流行起来了。漫长的8年时间,flask一直没有发布一个严肃的正式版本,但是却不能阻挡它成了github上最受好评的Python Web框架。

flask内核内置了两个最重要的组件,所有其它的组件都是通过易扩展的插件系统集成进来的。这两个内置的组件分别是werkzeug和jinja2。

werkzeug是一个用于编写Python WSGI程序的工具包,它的结构设计和代码质量在开源社区广受褒扬,其源码被尊为Python技术领域最值得阅读的开源库之一。

# wsgi.py
from werkzeug.wrappers import Request, Response@Request.application
def application(request):return Response('Hello World!')if __name__ == '__main__':from werkzeug.serving import run_simplerun_simple('localhost', 4000, application)
复制代码

运行python wsgi.py打开浏览器访问http://localhost:4000/就可以看到页面输出了Hello World!

Have you looked at werkzeug.routing? It's hard to find anything that's simpler, more self-contained, or purer-WSGI than Werkzeug, in general — I'm quite a fan of it!

by Alex Martelli, the author of 《Python in a Nutshell》 && 《Python Cookbook》

jinja2是一个功能极为强大的模板系统,它完美支持unicode中文,每个模板都运行在安全的沙箱环境中,使用jinja2编写的模板代码非常优美。

{% extends "layout.html" %}
{% block body %}<ul>{% for user in users %}<li><a href="{{ user.url }}">{{ user.username }}</a></li>{% endfor %}</ul>
{% endblock %}
复制代码

werkzeug和jinja2这两个库的共同特点是编写的代码赏心悦目,作者Armin Ronacher选择这两个库来作为flask的基石说明作者有非常挑剔的代码品味。那么作者是谁呢,铛!他是一位来自澳大利亚的帅哥!

好,闲话少说言归正传,接下来我们开始体验flask的神奇魅力。

安装flask

pip install flask

圆周率计算API

圆周率可以使用正整数的平方倒数之和求得,当这个级数趋于无限时,值会越来越接近圆周率。

# flask_pi.py
import mathfrom flask import Flask, requestapp = Flask(__name__)@app.route("/pi")
def pi():# 默认参数n = int(request.args.get('n', '100'))s = 0.0for i in range(1, n):s += 1.0/i/ireturn str(math.sqrt(6*s))if __name__ == '__main__':app.run()
复制代码

运行python flask_pi.py,打开浏览器访问http://localhost:5000/pi?n=1000000,可以看到页面输出3.14159169866,这个值同圆周率已经非常接近。

注意pi()的返回值不能是浮点数,所以必须使用str转换成字符串

再仔细观察代码,你还会注意到一个特殊的变量request,它看起来似乎是一个全局变量。从全局变量里拿当前请求参数,这非常奇怪。如果在多线程环境中,该如何保证每个线程拿到的都是当前线程正在处理的请求参数呢?所以它不能是全局变量,它是线程局部变量,线程局部变量外表上和全局变量没有差别,但是在访问线程局部变量时,每个线程得到的都是当前线程内部共享的对象。

缓存计算结果

为了避免重复计算,我们将已经计算的pi(n)值缓存起来,下次就可以直接查询。同时我们不再只返回一个单纯的字符串,我们返回一个json串,里面有一个字段cached用来标识当前的结果是否从缓存中直接获取的。

import math
import threadingfrom flask import Flask, request
from flask.json import jsonifyapp = Flask(__name__)class PiCache(object):def __init__(self):self.pis = {}self.lock = threading.RLock()def set(self, n, pi):with self.lock:self.pis[n] = pidef get(self, n):with self.lock:return self.pis.get(n)cache = PiCache()@app.route("/pi")
def pi():n = int(request.args.get('n', '100'))result = cache.get(n)if result:return jsonify({"cached": True, "result": result})s = 0.0for i in range(1, n):s += 1.0/i/iresult = math.sqrt(6*s)cache.set(n, result)return jsonify({"cached": False, "result": result})if __name__ == '__main__':app.run()
复制代码

运行python flask_pi.py,打开浏览器访问http://localhost:5000/pi?n=1000000,可以看到页面输出

{"cached": false,"result": 3.141591698659554
}
复制代码

再次刷新页面,我们可以观察到cached字段变成了true,说明结果确实已经缓存了

{"cached": true,"result": 3.141591698659554
}
复制代码

读者也许会问,为什么缓存类PiCache需要使用RLock呢?这是因为考虑到多线程环境下Python的字典读写不是完全线程安全的,需要使用锁来保护一下数据结构。

分布式缓存

上面的缓存仅仅是内存缓存,进程重启后,缓存结果消失,下次计算又得重新开始。

if __name__ == '__main__':app.run('127.0.0.1', 5001)
复制代码

如果开启第二个端口5001来提供服务,那这第二个进程也无法享受第一个进程的内存缓存,而必须重新计算。所以这里要引入分布式缓存Redis来共享计算缓存,避免跨进程重复计算,避免重启重新计算。

import math
import redisfrom flask import Flask, request
from flask.json import jsonifyapp = Flask(__name__)class PiCache(object):def __init__(self, client):self.client = clientdef set(self, n, result):self.client.hset("pis", str(n), str(result))def get(self, n):result = self.client.hget("pis", str(n))if not result:returnreturn float(result)client = redis.StrictRedis()
cache = PiCache(client)@app.route("/pi")
def pi():n = int(request.args.get('n', '100'))result = cache.get(n)if result:return jsonify({"cached": True, "result": result})s = 0.0for i in range(1, n):s += 1.0/i/iresult = math.sqrt(6*s)cache.set(n, result)return jsonify({"cached": False, "result": result})if __name__ == '__main__':app.run('127.0.0.1', 5000)
复制代码

运行python flask_pi.py,打开浏览器访问http://localhost:5000/pi?n=1000000,可以看到页面输出

{"cached": false,"result": 3.141591698659554
}
复制代码

再次刷新页面,我们可以观察到cached字段变成了true,说明结果确实已经缓存了

{"cached": true,"result": 3.141591698659554
}
复制代码

重启进程,再次刷新页面,可以看书页面输出的cached字段依然是true,说明缓存结果不再因为进程重启而丢失。

MethodView

写过Django的朋友们可能会问,Flask是否支持类形式的API编写方式,回答是肯定的。下面我们使用Flask原生支持的MethodView来改写一下上面的服务。

import math
import redisfrom flask import Flask, request
from flask.json import jsonify
from flask.views import MethodViewapp = Flask(__name__)class PiCache(object):def __init__(self, client):self.client = clientdef set(self, n, result):self.client.hset("pis", str(n), str(result))def get(self, n):result = self.client.hget("pis", str(n))if not result:returnreturn float(result)client = redis.StrictRedis()
cache = PiCache(client)class PiAPI(MethodView):def __init__(self, cache):self.cache = cachedef get(self, n):result = self.cache.get(n)if result:return jsonify({"cached": True, "result": result})s = 0.0for i in range(1, n):s += 1.0/i/iresult = math.sqrt(6*s)self.cache.set(n, result)return jsonify({"cached": False, "result": result})# as_view提供了参数可以直接注入到MethodView的构造器中
# 我们不再使用request.args,而是将参数直接放进URL里面,这就是RESTFUL风格的URL
app.add_url_rule('/pi/<int:n>', view_func=PiAPI.as_view('pi', cache))if __name__ == '__main__':app.run('127.0.0.1', 5000)
复制代码

我们实现了MethodView的get方法,说明该API仅支持HTTP请求的GET方法。如果要支持POST、PUT和DELETE方法,需要用户自己再去实现这些方法。

flask默认的MethodView挺好用,但是也不够好用,它无法在一个类里提供多个不同URL名称的API服务。所以接下来我们引入flask的扩展flask-classy来解决这个问题。

小试flask扩展flask-classy

使用扩展的第一步是安装扩展pip install flask-classy,然后我们在同一个类里再加一个新的API服务,计算斐波那契级数。

import math
import redisfrom flask import Flask
from flask.json import jsonify
from flask_classy import FlaskView, route  # 扩展app = Flask(__name__)# pi的cache和fib的cache要分开
class PiCache(object):def __init__(self, client):self.client = clientdef set_fib(self, n, result):self.client.hset("fibs", str(n), str(result))def get_fib(self, n):result = self.client.hget("fibs", str(n))if not result:returnreturn int(result)def set_pi(self, n, result):self.client.hset("pis", str(n), str(result))def get_pi(self, n):result = self.client.hget("pis", str(n))if not result:returnreturn float(result)client = redis.StrictRedis()
cache = PiCache(client)class MathAPI(FlaskView):    @route("/pi/<int:n>")def pi(self, n):result = cache.get_pi(n)if result:return jsonify({"cached": True, "result": result})s = 0.0for i in range(1, n):s += 1.0/i/iresult = math.sqrt(6*s)cache.set_pi(n, result)return jsonify({"cached": False, "result": result})    @route("/fib/<int:n>")def fib(self, n):result, cached = self.get_fib(n)return jsonify({"cached": cached, "result": result})def get_fib(self, n): # 递归,n不能过大,否则会堆栈过深溢出stackoverflowif n == 0:return 0, Trueif n == 1:return 1, Trueresult = cache.get_fib(n)if result:return result, Trueresult = self.get_fib(n-1)[0] + self.get_fib(n-2)[0]cache.set_fib(n, result)return result, FalseMathAPI.register(app, route_base='/')  # 注册到appif __name__ == '__main__':app.run('127.0.0.1', 5000)
复制代码

访问http://localhost:5000/fib/100,我们可以看到页面输出了

{"cached": false,"result": 354224848179261915075
}
复制代码

访问http://localhost:5000/pi/10000000,计算量比较大,所以多转了一回,最终页面输出了

{"cached": false,"result": 3.141592558095893
}
复制代码

高级文章,关注微信订阅号「码洞

扩展阅读

廖雪峰教你ThreadLocal的正确用法

Python字典是否是线程安全的

Hello Flask知乎专栏

Github上最受欢迎的Python轻量级框架Flask入门相关推荐

  1. 从GitHub中整理出来的15个最受欢迎的Python开源框架,你喜欢哪个

    从GitHub中整理出的15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python Web应用开发框架 Djang ...

  2. GitHub上最受欢迎的Android开源项目TOP20

    以下这些开源项目都是从GitHub上筛选的,我强烈推荐android程序源代码有时间的时候自己在上面淘淘,或许能发现自己须要的开源程序. 了解开源项目有两个优点: 1.借鉴代码,一般来说.火爆的开源项 ...

  3. GitHub上最受开发人员欢迎的5大Java项目

    GitHub上有很多Java项目(准确地说,大概是744K个),但是开发人员最兴奋的是哪些项目?今天,小千列举了GitHub上一些最流行的Java项目.从Mockitos到Guava,以及 java- ...

  4. 5月份 Github 上最热的十个 Python 项目,从Debug工具到AI水军、量化交易系统。

    2019 年第 46 篇,总第 70 篇文章 原文地址:https://medium.mybridge.co/python-open-source-for-the-past-month-v-may-2 ...

  5. GitHub 上最受欢迎的 5 大 Java 项目

    导读:GitHub 上有大约有 744K 多的 Java 项目,但是最让开发人员感兴趣的有哪些项目?本文列举了 GitHub 上最受欢迎的 5 个 Java 项目,从 Mockitos 到 Guava ...

  6. GitHub上广受欢迎的下载神器:youtube-dl

    一个「身价」7.5 万星星的 GitHub 项目,从受热捧,到被起诉下架,再到引发社区热议. 故事本已一波三折. 但最近,关于这个项目的最新「剧情」,又有了重大更新. GitHub 官方宣布:拒绝投诉 ...

  7. 可能是github上最受欢迎的五子棋AI

    老苏突然很想玩五子棋,然后就在 github 上找到了这个项目,看起来有将近一年没更新了,凑合玩吧 用作者的话说,可能是 github 上最受欢迎的 五子棋AI,但是从其他人的反馈看应该是有点夸张了 ...

  8. GitHub上最受欢迎的 5 大 Java 项目

    1. Mockito Mockito 并不是无酒精混合饮料的意思.Mockito 是一个针对 Java 的 mocking 框架.它与 EasyMock 和jMock 很相似,但是通过在执行后校验什么 ...

  9. 一览GitHub上最受程序欢迎的5大Java开源项目

    GitHub上有很多Java项目.(准确地说,大概是744K个).但是开发人员最兴奋的是哪些项目?今天,小编列举了GitHub上一些最流行的Java项目.从Mockitos到Guava,以及 java ...

最新文章

  1. 脚本文件直接执行python代码
  2. 线程属性--十分重要的概念
  3. java处理日期时间 相加减
  4. android 电量控件,Android实现显示电量的控件代码
  5. [转]Java8-本地缓存
  6. fwrite 写不进去_12款猫狗粮对比,吃中低端猫狗粮吃得多不省钱,还更容易得病...
  7. 计算机技能大赛试题及答案,全国中职计算机技能大赛(园区网)试题及参考答案...
  8. 多用途响应式ppt资源下载平台-html模板
  9. 王琪你计算机学院,计算机学院“计忆时光”2019元旦联欢会暨年度颁奖典礼圆满举行...
  10. linux收集完整技术支持信息的命令有,Linux下常用的日志收集命令(RedhatSuSe)
  11. 华为下一代机皇曝光:全新麒麟985+55W超级快充
  12. VS C#/C++ 工具箱显示出来 工具箱不见了
  13. 如何编译符合自己路由器的的OpenWrt固件
  14. 机器学习 | 交叉验证
  15. 1483. 纪念品分组 输出好忧桑…………
  16. 十四届蓝桥青少组模拟赛Python-20221108
  17. 关于云音乐数据治理的实践与思考
  18. xpath.extract() 的使用
  19. 【文献翻译】Epileptic Seizures Detection Using Deep Learning Techniques: A Review
  20. 【backtrader保姆级教学】日内区间突破型策略

热门文章

  1. iOS扩大按钮的点击范围
  2. 解决移动端 手机号input 属性为 number,maxlength无效情况
  3. Emule使用Upnp,解决Lowid和port not reachable的问题
  4. BZOJ 3391: [Usaco2004 Dec]Tree Cutting网络破坏(搜索)
  5. 深入理解STM32内存管理
  6. python中hashmap的方法_如何为Java的HashMap模拟Python的dict的“ items(...
  7. 如何理解Mysql的索引及他们的原理--------二叉查找树和平衡二叉树和B树和B+树
  8. java判断当前时间距离第二天凌晨的秒数
  9. linux移动文件 rf参数_linux下文件的复制、移动与删除命令为:cp,mv,rm
  10. mysql php pdo例_PHP的PDO操作实例