日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

我叫駱駝

會(huì)點(diǎn)兒代碼,會(huì)點(diǎn)兒讀書

這世上的書浩如煙海

我能做的就是盡量整理分享給你

在上一節(jié)Flask項(xiàng)目實(shí)戰(zhàn)第一彈中我們講到了路由,先看下面代碼回顧一下:

from flask import Flask

App = Flask(__name__)


@app.route("/hello")
def hello():
    return "<p>Hello World ...</p>"


if __name__ == '__main__':
    app.run(load_dotenv=True)

@app.route("/hello") 就是裝飾器。交流群里小伙伴問我,Python/ target=_blank class=infotextkey>Python里的裝飾器該怎么理解,今天我們好好嘮嘮這個(gè)東西。

說到裝飾器,我們不得不談一個(gè)知識點(diǎn):閉包。我們從代碼入手,一點(diǎn)一點(diǎn)來說閉包。

Python 有一個(gè)好玩的地兒,就是 def 函數(shù)(exterior)內(nèi)部可以嵌套另一個(gè) def 函數(shù)(interior)。調(diào)用 exterior 時(shí),若遇到 interior , 僅僅完成對于 interior 的定義,而不去運(yùn)行 interior。如果 exterior return interior,那么我們可以使用 interior () 去調(diào)用 內(nèi)部函數(shù) interior 函數(shù)。

 var = 0


def exterior():
    var = 1

    def interior():
        print(var)
    return interior()  # 這里返回 interior 函數(shù)調(diào)用結(jié)果


exterior() # 打印 1

從上面代碼和結(jié)果中可以看到,interior 打印的 var 值 并非 第一行的 var。這說明,exterior 中的嵌套變量 var 覆蓋了全局變量var=0,然后 interior 中的本地變量按照引用規(guī)則,就引用了var = 1。

接下來,我們仔細(xì)想想下面這句話:

interior 作用域在函數(shù)結(jié)束后就立即失效,而exterior嵌套作用域在 interior 的函數(shù)返回后卻仍然有效。

var = 0


def exterior():
    var = 1

    def interior():
        print(var)
    return interior  # 這里返回 interior 函數(shù)對象


inter = exterior() 
inter()  # 打印 1

看完上面代碼,再思考一下剛剛的話。如果還不清楚,看下圖:

圖解

創(chuàng)建一個(gè)閉包必須滿足以下幾點(diǎn):

  • 必須有一個(gè)內(nèi)嵌函數(shù)
  • 內(nèi)嵌函數(shù)必須引用外部函數(shù)中的變量
  • 外部函數(shù)的返回值必須是內(nèi)嵌函數(shù)

轉(zhuǎn)

現(xiàn)在有了閉包的知識點(diǎn),我們再聊聊裝飾器(decorator)。我要掰開了揉碎了來說說裝飾器。

剛剛接觸裝飾器的同學(xué)會(huì)對這個(gè)概念感到迷茫,然后你在網(wǎng)上(尤其 csdn)找例子或者教程,基本千篇一律,或者講解的“點(diǎn)到為止”,你在看完之后,或許更迷茫了。

函數(shù)是什么

在說裝飾器前,我們聊聊 Python 的函數(shù)。眾所周知:在 Python 中,一切皆對象,函數(shù)是一等對象。

編程語言理論家把“一等對象”定義為滿足下述條件的程序?qū)嶓w:

  • 在運(yùn)行時(shí)創(chuàng)建①
  • 能賦值給變量或者數(shù)據(jù)結(jié)構(gòu)中的元素②
  • 能作為參數(shù)傳遞給函數(shù)③
  • 能作為函數(shù)的返回結(jié)果④

我們看這么一段程序:

def double(x: int) -> int:
    return x * 2

這段代碼很簡單,計(jì)算了一個(gè)整數(shù)的2倍。那么我么用 dis 模塊進(jìn)行反編譯,看看他是怎么運(yùn)行的。

>>>from my_test import double
>>>from dis import dis

>>>dis(double)  # 結(jié)果如下

源碼行號

指令在函數(shù)中的偏移

指令符號

指令參數(shù)

實(shí)際參數(shù)值

2

0

LOAD_FAST

0

x

 

2

LOAD_CONST

1

2

 

4

BINARY_MULTIRLY

 

 

 

6

RETURN_VALUE

 

 

指令符號解釋:

  • LOAD_FAST :一般加載局部變量的值,也就是讀取值,用于計(jì)算或者函數(shù)調(diào)用傳參等;
  • LOAD_CONST :加載 const 變量,比如數(shù)值、字符串等等;
  • BINARY_MULTIRLY:見名知意,二進(jìn)制乘法
  • RETURN_VALUE:返回值

結(jié)合反編譯的結(jié)果,仔細(xì)理解一下代碼的運(yùn)行流程。下面我們看另外一個(gè)例子:

def double(x: int) -> int:
    return x * 2


def triple(x: int) -> int:
    return x * 3


def call_func(func, x: int) -> int:
    return func(x)


result = call_func(triple, 2)
print(result)
dis(call_func)

源碼行號

指令在函數(shù)中的偏移

指令符號

指令參數(shù)

實(shí)際參數(shù)值

10

0

LOAD_FAST

0

func

 

2

LOAD_FAST

1

x

 

4

CALL_FUNCTION

1

 

 

6

RETURN_VALUE

 

 

在運(yùn)行過程中:出現(xiàn)了 CALL_FUNCTION 。結(jié)合第13行代碼,仔細(xì)體會(huì)一下這句話:函數(shù)能作為參數(shù)傳遞給另外一個(gè)函數(shù)。

我們現(xiàn)在看一下閉包的執(zhí)行流程

def call_func():
    def double(x: int) -> int:
        return x * 2

    return double

dis(call_func)

里邊出現(xiàn)了一個(gè)關(guān)鍵詞:MAKE_FUNCTION,見名知意,創(chuàng)建函數(shù)。此時(shí)再回想“一等對象”所滿足的條件。

說了這么多,無非是想告訴大家一個(gè)重要的東西,函數(shù)就是對象,可以被另一個(gè)函數(shù)返回,可以被賦值,也可以被調(diào)用。

其實(shí)到這里,才真是說完閉包這個(gè)東西。裝飾器和閉包大同小異,下面我們接著來。

裝飾器

有這種一種等價(jià)語法:

def callfunc(func):
    return 1


@callfunc
def triple(x: int) -> int:
    return x * 3

等價(jià)于

def callfunc(func):
    return 1


def triple(x: int) -> int:
    return x * 3


triple = callfunc(triple)

無論上面那種方式,我們輸出的 tripre 這個(gè)對象的值都是 1

>>> print(triple)
>>> 1

所以,閉包可以寫成@這種形式呢?其實(shí),裝飾器可以理解為閉包的一種,我們可以這樣認(rèn)為:閉包傳遞的是變量,而裝飾器傳遞的是函數(shù),除此之外沒有任何區(qū)別。

我們看一個(gè)打印時(shí)間的裝飾器:

import time


def timeit(func):
    def wrapper(x):
        start = time.time()
        ret = func(x)
        print(time.time() - start)
        return ret

    return wrapper


@timeit
def my_func(x):
    time.sleep(x)


my_func(1)

timeit 裝飾器就打印 my_func 函數(shù)的運(yùn)行時(shí)間。是不是在了解完閉包之后很簡單了。

裝飾器的作用就是:在不改變原函數(shù)的情況下,對已有函數(shù)進(jìn)行額外的功能擴(kuò)展。

恭喜你,Python 技能又進(jìn)一步。

回到 Flask 我們看看路由裝飾器

Flask 中路由的裝飾器很簡單,我們以 route 為例,以下是 route 函數(shù)源碼(抽離版):

import typing as t


def add_url_rule(rule, endpoint, f, param):
    pass


def route(rule: str, **options: t.Any) -> t.Callable:

    def decorator(f: t.Callable) -> t.Callable:
        endpoint = options.pop("endpoint", None)
        add_url_rule(rule, endpoint, f, **options)
        return f

    return decorator

route 函數(shù)就是一個(gè)裝飾器,內(nèi)部嗲用 add_url_rule 實(shí)現(xiàn)真正的路由添加。再回過頭看看裝飾器的作用和定義以及使用,是不是明白了許多!加油,慢就快,快就是慢。

分享到:
標(biāo)簽:Python
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定