欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

程序员文章站 2022-07-06 12:34:44
flask最近终于发布了它的1.0版本更新,从项目开源到最近的1.0版本flask已经走过了8个年头。 运行 python app.py ,打开浏览器访问 http://localhost:5000/ 就可以看到页面输出了 Hello World! flask的诞生于2010年的愚人节,本来它只是作 ......

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。

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

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

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

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的基石说明作者有非常挑剔的代码品味。那么作者是谁呢,铛!他是一位来自澳大利亚的帅哥!

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

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

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

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

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

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

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

import math
import threading
from flask import Flask, request
from flask.json import jsonify
app = 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] = pi
 def 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.0
 for i in range(1, n):
 s += 1.0/i/i
 result = 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
}
Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

import math
import redis
from flask import Flask, request
from flask.json import jsonify
app = Flask(__name__)
class PiCache(object):
 def __init__(self, client):
 self.client = client
 def 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:
 return
 return 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.0
 for i in range(1, n):
 s += 1.0/i/i
 result = 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 redis
from flask import Flask, request
from flask.json import jsonify
from flask.views import MethodView
app = Flask(__name__)
class PiCache(object):
 def __init__(self, client):
 self.client = client
 def 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:
 return
 return float(result)
client = redis.StrictRedis()
cache = PiCache(client)
class PiAPI(MethodView):
 def __init__(self, cache):
 self.cache = cache
 def get(self, n):
 result = self.cache.get(n)
 if result:
 return jsonify({"cached": True, "result": result})
 s = 0.0
 for i in range(1, n):
 s += 1.0/i/i
 result = 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服务,计算斐波那契级数。

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程

 

import math
import redis
from 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 = client
 def 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:
 return
 return 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:
 return
 return 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.0
 for i in range(1, n):
 s += 1.0/i/i
 result = 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不能过大,否则会堆栈过深溢出*
 if n == 0:
 return 0, True
 if n == 1:
 return 1, True
 result = cache.get_fib(n)
 if result:
 return result, True
 result = self.get_fib(n-1)[0] + self.get_fib(n-2)[0]
 cache.set_fib(n, result)
 return result, False
MathAPI.register(app, route_base='/') # 注册到app
if __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
}

欢迎加入千人交流学习答疑群:125240963

Github上最受欢迎的Python轻量级框架Flask入门!史上最详细教程