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.pswp.cn/news/277852.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/277852.shtml
英文地址,請注明出處:http://en.pswp.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;測…