Python的Flask框架中@app.route的用法教程
在我上一篇文章,我搭了一個(gè)框架,模擬了Flask網(wǎng)站上“@app.route(‘/')”第一條例子的行為。
如果你錯(cuò)過(guò)了那篇“這不是魔法”,請(qǐng)點(diǎn)擊這里。
在這篇文章中,我們打算稍微調(diào)高點(diǎn)難度,為我們的URL加入可變參數(shù)的能力,在本文的最后,我們將支持下述代碼段所期望達(dá)到的行為。
app = Flask(__name__) @app.route("/hello/<username>") def hello_user(username): return "Hello {}!".format(username)
這樣下面的路徑實(shí)例(path):
/hello/ains
將會(huì)匹配上面的路徑,給我們的輸出為
Hello ains!
以正則的形式表達(dá)我們的路徑。
現(xiàn)在我們將允許我們的URL動(dòng)態(tài)變化,我們不再能夠?qū)⒂孟惹笆褂谩癅app.route()”注冊(cè)的路徑直接與路徑實(shí)例比較。
我們將用什么替代?我們需要用上正則表達(dá)式,這樣我們就可以將路徑作為一種模式進(jìn)行匹配,而不和一條固定的字符串比較了。
我不打算在本文展開(kāi)討論正則表達(dá)式的細(xì)節(jié),不過(guò)如果你需要一份復(fù)習(xí)資料,可以點(diǎn)擊這個(gè)網(wǎng)站。
那么,我們的第一步是將我們的路徑轉(zhuǎn)化成正則表達(dá)式模式,這樣我們就能在輸入路徑實(shí)例時(shí)進(jìn)行匹配。我們也將使用這個(gè)正則表達(dá)式提取我們感興趣的變量。
那么,匹配路徑”/hello/”的正則表達(dá)式該長(zhǎng)啥樣呢?
嗯一個(gè)簡(jiǎn)單的正則表達(dá)式譬如“^/hello/(.+)$”將是個(gè)好的開(kāi)始,讓我們一起看看它和代碼是怎么一起工作的:
import re route_regex = re.compile(r"^/hello/(.+)$") match = route_regex.match("/hello/ains") print match.groups()
將會(huì)輸出:
('ains',)
不錯(cuò),不過(guò),理想情況是我們想要維護(hù)我們已經(jīng)匹配上的第一組鏈接,并且從路徑“/hello/”識(shí)別出“username”。
命名捕獲組
幸運(yùn)的是,正則表達(dá)式也支持命名捕獲組,允許我們給匹配組分配一個(gè)名字,我們能在讀取我們的匹配之后找回它。
我們可以使用下述符號(hào),給出第一個(gè)例子識(shí)別“username”的捕獲組。
/hello/(<?P<username>.+)"
然后我們可以對(duì)我們的正則表達(dá)式使用groupdict()方法,將所有捕獲組當(dāng)作一個(gè)字典,組的名字對(duì)應(yīng)匹配上的值。
那么我們給出下述代碼:
route_regex = re.compile(r'^/hello/(?P<username>.+)$') match = route_regex.match("/hello/ains") print match.groupdict()
將為我們輸出以下字典:
{'username': 'ains'}
現(xiàn)在,有了我們所需要的正則表達(dá)式的格式,以及如何使用它們?nèi)テヅ漭斎氲腢RLs的知識(shí),最后剩下的是寫(xiě)一個(gè)方法,將我們聲明的路徑轉(zhuǎn)換成它們等價(jià)的正則表達(dá)式模式。
要做這個(gè)我們將使用另一個(gè)正則表達(dá)式(接下來(lái)將全是正則表達(dá)式),為了讓我們路徑中的變量轉(zhuǎn)換成正則表示式模式,那這里作為示范我們將將“”轉(zhuǎn)換成“(?P.+)”。
聽(tīng)起來(lái)太簡(jiǎn)單了!我們將可以只用一行新代碼實(shí)現(xiàn)它。
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>')
這里我們用一個(gè)正則表達(dá)式代表所有出現(xiàn)的模式(一個(gè)包含在尖括號(hào)中的字符串),與它的正則表達(dá)式命名組等價(jià)。
re.sub的第一個(gè)參數(shù) 我們將我們的模式放進(jìn)括號(hào),目的是把它分配到第一個(gè)匹配組。在我們的第二個(gè)參數(shù),我們可以使用第一匹配組的內(nèi)容,方法是寫(xiě)1(2將是第二匹配組的內(nèi)容,以此類推…….)
那么最后,輸入模式
/hello/<username>
將給我們正則表達(dá)式:
^/hello/(?P<username>.+)$
推陳出新
讓我們掃一眼上次我們寫(xiě)的簡(jiǎn)單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!"
現(xiàn)在我們有一個(gè)新的改進(jìn)方法用來(lái)匹配輸入的路徑,我們打算移除我們上一版實(shí)現(xiàn)時(shí)用到的原生字典。
讓我們從改造我們的函數(shù)著手,以便于添加路徑,這樣我們就可以用(pattern, view_function)對(duì)列表代替字典保存我們的路徑。
這意味著當(dāng)一個(gè)程序員使用@app.route()裝飾一個(gè)函數(shù),我們將要嘗試將他們的路徑編譯變成一個(gè)正則表達(dá)式,然后存儲(chǔ)它,屬于一個(gè)在我們新的路徑列表里的裝飾函數(shù)。
讓我們看看實(shí)現(xiàn)代碼:
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
我們也打算需要一個(gè)get_route_match方法,給它一個(gè)路徑實(shí)例,將會(huì)嘗試并找到一個(gè)匹配的view_function,或者返回None如果一個(gè)也找不到的話。
然而,如果找了到匹配的話,除了view_function之外,我們還需要返回一個(gè)東西,那就是我們包含之前捕獲匹配組的字典,我們需要它來(lái)為視圖函數(shù)傳遞正確的參數(shù)。
好了我們的get_route_match大概就長(zhǎng)這樣:
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
現(xiàn)在我們快要完成了,最后一步將是找出調(diào)用view_function的方法,使用來(lái)自正則表達(dá)式匹配組字典的正確參數(shù)。
調(diào)用一個(gè)函數(shù)的若干種方法
讓我們回顧一下不同的方法調(diào)用一個(gè)python的函數(shù)。
比如像這樣:
def hello_user(username): return "Hello {}!".format(username)
最簡(jiǎn)單的(也許正是你所熟知的)辦法是使用正則參數(shù),在這里參數(shù)的順序匹配我們定義的那些函數(shù)的順序。
>>> hello_user("ains") Hello ains!
另一種方法調(diào)用一個(gè)函數(shù)是使用關(guān)鍵詞參數(shù)。關(guān)鍵詞參數(shù)可以通過(guò)任何順序指定,適合有許多可選參數(shù)的函數(shù)。
>>> hello_user(username="ains") Hello ains!
在Python中最后一種調(diào)用一個(gè)函數(shù)的方法是使用關(guān)鍵詞參數(shù)字典,字典中的關(guān)鍵詞對(duì)應(yīng)參數(shù)名稱。我們告訴Python解包一個(gè)字典,并通過(guò)使用兩個(gè)星號(hào)“**”來(lái)把它當(dāng)作函數(shù)的關(guān)鍵詞參數(shù)。 下面的代碼段與上面的代碼段完全一樣,現(xiàn)在我們使用字典參數(shù),我們可以在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建它。
>>> kwargs = {"username": "ains"} >>> hello_user(**kwargs) Hello ains!
好了,還記得上面的groupdict()方法?就是那個(gè)同樣的在正則表達(dá)式完成匹配后返回{“username”: “ains”}的家伙?那么現(xiàn)在我們了解了kwargs,我們能很容易向我們的view_function傳遞字典匹配,完成NotFlask!
那么讓我們把這些都塞進(jìn)我們最終的類中。
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))
接下來(lái),就是見(jiàn)證奇跡的時(shí)刻,請(qǐng)看下面代碼段:
app = NotFlask() @app.route("/hello/") def hello_user(username): return "Hello {}!".format(username) print app.serve("/hello/ains")
我們將得到輸出:
Hello ains!
相關(guān)文章
Python實(shí)現(xiàn)圖書(shū)借閱管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)圖書(shū)借閱管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03pycharm打包py項(xiàng)目為.exe可執(zhí)行文件的兩種方式
本文主要介紹了pycharm打包py項(xiàng)目為.exe可執(zhí)行文件的兩種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01Python-GUI?wxPython之自動(dòng)化數(shù)據(jù)生成器的項(xiàng)目實(shí)戰(zhàn)
本文主要介紹了Python-GUI?wxPython之自動(dòng)化數(shù)據(jù)生成器實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05python用dataframe將csv中的0值數(shù)據(jù)轉(zhuǎn)化為nan缺失值字樣
本文主要介紹了python用dataframe將csv中的0值數(shù)據(jù)轉(zhuǎn)化為nan缺失值字樣,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08單鏈表反轉(zhuǎn)python實(shí)現(xiàn)代碼示例
這篇文章主要介紹了單鏈表反轉(zhuǎn)python實(shí)現(xiàn),分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02Python如何快速上手? 快速掌握一門新語(yǔ)言的方法
Python如何快速上手? 這篇文章主要為大家詳細(xì)介紹了快速掌握一門新語(yǔ)言的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11