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

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知乎专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/277852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql表关联

mysql的表关联&#xff1a; left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录 inner join(等值连接) 只返回两个表中联结字段相等的行 转载于:https://www.cnblogs.com/bloghua…

开发第一个Meeting App

今天我们来看一下如何使用c# net6 来开发一个 teams的 meeting app。首先先确保本地安装了最新版本的 net6。创建一个目录&#xff0c;然后在目录下输入下面的命令行&#xff0c;让 dotnet 来生成一个初步的asp.net core 代码框架。 dotnet new razor代码框架生成如下的目录和…

solrCloud搭建

一.什么是solrCloud&#xff1f; SolrCoud(solr云)是solr提供的分布式搜索方案。当一个系统搜索请求并发很高的时候&#xff0c;就需要使用solrCloud来满足这些需求 solrCloud是基于solr和zookeeper的分布式的搜索方案&#xff0c;它的主要思想是使用zookeeper作为挤集群配置的…

Linux 添加新用户账号并赋予root权限

除了root用户之外&#xff0c;通常需要为每个管理创建各自的用户账号&#xff0c;方便每个管理员登录使用&#xff0c; 步骤如下&#xff1a; 1. 添加新用户账号 useradd mary.lee 2. 为新用户账号设置密码 passwd mary.lee 3. 为新用户账号赋予root权限 usermod -a -G root…

如何获取 Teams Meeting 的上下文信息

我们上一篇文章讲了如果使用 net6 和 c# 来快速开发一个最简单的 teams meeting app。为了让大家比较容易理解&#xff0c;上个sample非常简单&#xff0c;简单到没有什么功能&#xff0c;那我们现在就来慢慢扩展这个app的功能&#xff1a;看看如何获取 meeting 的上下文。 打…

php的运行流程

1、Zend引擎&#xff1a;Zend整体用纯C实现&#xff0c;是PHP的内核部分&#xff0c;他将PHP代码翻译&#xff08;词法、语法解析等一系列编译过程&#xff09;为可执行opcode的处理并实现相应的处理方法、实现了基本的数据结构&#xff08;如&#xff1a;hashtable、OO&#x…

内置方法

isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object):pass obj Foo() isinstance(obj, Foo) issubclass(sub, super)检查sub类是否是 super 类的派生类 class Foo(object):pass class Bar(Foo):pass issubclass…

会议中的Meeting App

接着我们上两篇博客文章&#xff0c;我们说了如何开发会议前和会议后的 meeting app&#xff0c;那如何开发一个会议中的 app 呢&#xff0c;实际上比较简单&#xff0c;我们只需要在 tab 的配置项中勾选下面这两个选项即可。 勾选后&#xff0c;我们安装app到我们的一个会议中…

0-2岁的app开发人员必读,Android开发APP前的准备事项

2019独角兽企业重金招聘Python工程师标准>>> 随着移动互联网的兴起&#xff0c;各行各业对移动应用的需求越来越大&#xff0c;从事APP开发的人也越来越多&#xff0c;APP开发行业可以说是方兴未艾。APP开发是比较复杂的事情&#xff0c;涉及产品、美工设计、服务器…

FixedThreadPool吞掉了异常

为了方便遍描述问题&#xff0c;如下是简化后的 public class RunException {public static void main(String[] args) { ExecutorService readerPool Executors.newFixedThreadPool(3); readerPool.submit(new Runnable() { public void run() { throw new RuntimeException(…

Teams Meeting App的 task 弹出框

前几篇文章我们介绍了 Teams Meeting App 的各种类型和如何从无到有的使用 net6 和 c# 来开发一个 Teams Meeting app&#xff0c;那今天我们开始讨论一些 meeting app 的高级互动&#xff1a; task 弹出框。我们先来快速修改一下之前的代码&#xff0c;看看什么是 task 弹出框…

react 学习

react官网地址&#xff1a;http://facebook.github.io/react/ webpack官网地址&#xff1a;http://webpack.js.org/ 英文 https://www.webpackjs.com/ 中文 参考资料&#xff1a; React 入门实例教程-阮一峰 webpack的学习 学习列表&#xff1a; 了解react的语法&#x…

如何获取Teams Meeting 详情

最近有一些朋友问我&#xff0c;有没有可能获取到会议的详情&#xff0c;我搜索了目前所有的 teams 文档&#xff0c;发现有一个api可以获取&#xff0c;不过在我写这篇文章的时候&#xff0c;这个 api 还在 preview 阶段&#xff0c;可能在正式发布前&#xff0c;还会有一些变…

C++ : 内联函数和引用变量

一.内联函数 内联函数和普通函数的使用方法没有本质区别&#xff0c;我们来看一个例子&#xff0c;下面展示了内联函数的使用方法&#xff1a; #include <iostream> using namespace std; //下面展示内联函数的使用 inline double square(double x) {return (x*x);} int…

Teams Meeting 实时事件通知

Microsoft Teams最近推出了很多新的功能和api&#xff0c;我们今天就来一起看一下 teams 会议的实时事件通知&#xff0c;我觉得有了这个功能&#xff0c;我们的app&#xff0c;我们的bot又可以有很多可以实现的场景了。 我们来看看如何在 c# 里处理会议开始和结束这两个事件。…

error记录 | 不能将参数 1 从“const char [5]”转换为“LPCTSTR

Windows使用两种字符集ANSI和UNICODE&#xff0c;前者就是通常使用的单字节方式&#xff0c;但这种方式处理象中文这样的双字节字符不方便&#xff0c;容易出现半个汉字的情况。而后者是双字节方式&#xff0c;方便处理双字节字符。Windows NT的所有与字符有关的函数都提供两…

JMM 学习笔记

并发编程的模型 并发编程需要解决的两个问题&#xff1a;线程之间如何同步&#xff0c;线程之间如何通信。 线程之间通信&#xff1a;共享内存&#xff0c;消息传递。 共享内存通过线程之间读-写程序的公共状态进行通信。消息传递要通过线程之间主动传递消息进行通信。 线程之间…

嵌套函数,匿名函数,高阶函数

目录 嵌套函数匿名函数高阶函数嵌套函数 就是在函数里再定义一个函数 # 1,函数内部可以在定义函数 # 2,函数要想执行&#xff0c;必须要先被调用 def name1():print(kk)def name2():print(vfx)name2() name1() 输出&#xff1a; kk vfx name2 现在他内部代码找输出&#xff0c;…

Teams Developer Portal介绍

在去年的 Build2021 大会上讲到的 Teams Developer Portal 已经上线一段时间了&#xff0c;我这几天玩了一下&#xff0c;发现比之前的 app studio 强大了很多&#xff0c;所以赶快写篇文章和大家分享。 Developer Portal 有两种访问的方式&#xff0c;一个是网页版&#xff0…

使用环境变量来配置 Teams App 的 manifest

上篇文章我们介绍了 Teams 的 Developer Portal&#xff0c;今天我想分享一个dev portal里一个比较实用的功能。这个功能在之前的 App Studio 里没有。这个功能叫 Environment variables。 当我们真实开发一个 teams app的时候&#xff0c;肯定有自己的开发环境&#xff0c;测…