成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

python裝飾器的原理和使用

goji / 1226人閱讀

摘要:我們以測(cè)量函數(shù)運(yùn)行時(shí)間為例來(lái)講一講裝飾器的運(yùn)行原理。三更加通用的裝飾器前面兩部分講了裝飾器的原理,這一部分就講講要寫出一個(gè)通用的裝飾器需要注意的問題。首先就是參數(shù)的問題,裝飾器返回的函數(shù)不是原來(lái)的函數(shù),函數(shù)的簽名也就和原來(lái)的函數(shù)簽名不一樣。

一、最簡(jiǎn)單的裝飾器

裝飾器是python中很基礎(chǔ)也很實(shí)用的一個(gè)特性。通過裝飾器我們可以很方便地為一些函數(shù)添加相同的功能。我們以測(cè)量函數(shù)運(yùn)行時(shí)間為例來(lái)講一講python裝飾器的運(yùn)行原理。

1、使用裝飾器打印函數(shù)運(yùn)行時(shí)間

通常我們使用 time 庫(kù)來(lái)獲取函數(shù)的運(yùn)行時(shí)間。

import time

# 函數(shù)運(yùn)行 2秒
def func():
    time.sleep(2)

start_time = time.time()
func()
end_time = time.time()
print("函數(shù)運(yùn)行時(shí)間為:%.2fs" % (end_time - start_time)) 

so easy,現(xiàn)在我們來(lái)思考一下:如果我們有 100個(gè)函數(shù),怎么打印這100個(gè)函數(shù)的運(yùn)行時(shí)間呢?

總不能按照上面的寫法來(lái)100次吧,這絕不是一個(gè)程序猿應(yīng)該做的事,這時(shí)候裝飾器就可以排上用場(chǎng)了。

我們先看代碼,然后再慢慢講其中的原理。

import time


def timeit(func):
    def result():
        start_time = time.time()
        func()
        end_time = time.time()
        print("函數(shù)運(yùn)行時(shí)間為:%.2fs" % (end_time - start_time))
    return result

@timeit
def func_0():
    time.sleep(2)
    
# 省略98個(gè)

@timeit
def func_99():
    time.sleep(2)

# 接下來(lái)直接調(diào)用這100個(gè)函數(shù)即可
func_0()
# ...

使用了 timeit 裝飾器的函數(shù)和沒使用裝飾器的原始方法效果一樣,不過這只是裝飾器最簡(jiǎn)單的一個(gè)應(yīng)用,我們下面來(lái)看看其中的原理。

2、timeit的運(yùn)行原理

首先,大家需要知道在python中的函數(shù)也是對(duì)象,是對(duì)象就可以作為參數(shù)傳遞,這是裝飾器實(shí)現(xiàn)的基礎(chǔ)。

接下來(lái)我們來(lái)看看裝飾器 timeit,不難看出 timeit 是一個(gè)函數(shù)。準(zhǔn)確地說 timeit 是一個(gè)接受一個(gè)函數(shù)為參數(shù)并且返回一個(gè)函數(shù)的函數(shù)。

聽著挺拗口的,別急,看我細(xì)細(xì)道來(lái)。

timeit 是一個(gè)函數(shù),它接受一個(gè)參數(shù),這個(gè)參數(shù)必須是函數(shù)(或者說可調(diào)用對(duì)象),并且 timeit 的返回結(jié)果一定是一個(gè)函數(shù)。

看到這里大家應(yīng)該對(duì)裝飾器有一點(diǎn)了解了,原來(lái)裝飾器就是接受一個(gè)函數(shù)為參數(shù)返回另一個(gè)函數(shù)的函數(shù)。

現(xiàn)在我們就可以解釋 timeit 的運(yùn)行原理了,看下面的代碼

# 代碼塊1
@timeit
def func_0():
    time.sleep(2)
    
# 代碼塊2
def func_0():
    time.sleep(2)
func_0 = timeit(func_0)

這里的代碼塊1和代碼塊2完全等價(jià),注意完全兩個(gè)字,這說明這兩段代碼的效果一模一樣。

事實(shí)上代碼塊1就是代碼塊2的簡(jiǎn)寫形式,因?yàn)榇a塊2的寫法挺麻煩的,所以python的設(shè)計(jì)者加了一些語(yǔ)法糖,就有了代碼塊1的寫法。

了解了這些我們?cè)賮?lái)看看 timeit 內(nèi)部

def timeit(func):
    def result():
        start_time = time.time()
        func()
        end_time = time.time()
        print("函數(shù)運(yùn)行時(shí)間為:%.2fs" % (end_time - start_time))
    return result

timeit里面我們定義了一個(gè)函數(shù) result ,函數(shù) result 里面的內(nèi)容我們很熟悉,就是打印函數(shù) func 的運(yùn)行時(shí)間。這里的 func 就是我們要裝飾的函數(shù)。

最后我們將函數(shù) result 返回,我們?cè)倏吹酱a塊2,返回的 result 函數(shù)替換了我們要裝飾的函數(shù) func_0,這時(shí)當(dāng)我們?cè)僬{(diào)用函數(shù) func_0 時(shí)其實(shí)就是在調(diào)用函數(shù) result

3、什么是閉包

我們看一段代碼:

def outer():
    a = 0
    def inner():
        print(a)
    return inner

func = outer()
func()

上面的函數(shù)和裝飾器很像,以前學(xué)C語(yǔ)言的可能會(huì)疑惑為什么變量a在函數(shù) outer 調(diào)用完成后仍然能夠被訪問。

這和python中變量的回收機(jī)制有關(guān),python根據(jù)變量的引用計(jì)數(shù)來(lái)判斷是變量是否需要回收。

當(dāng)一個(gè)對(duì)象的引用被創(chuàng)建或復(fù)制時(shí),對(duì)象的引用計(jì)數(shù)加1;當(dāng)一個(gè)對(duì)象的引用被銷毀時(shí),對(duì)象的引用計(jì)數(shù)減1,如果對(duì)象的引用計(jì)數(shù)減少為0,將對(duì)象的所占用的內(nèi)存釋放。

上面的例子中因?yàn)閷?duì)象a的引用一直沒有被銷毀引用計(jì)數(shù)不為0,所以在函數(shù) inner 里一直可以訪問對(duì)象a的值。

而且我們發(fā)現(xiàn)在調(diào)用過函數(shù) outer 后只有函數(shù) inner 可以訪問對(duì)象a。

這就相當(dāng)于為函數(shù) inner添加了一個(gè)私有的命名空間,而且只有函數(shù) inner 可以訪問這個(gè)命名空間,這就是python中的閉包。

裝飾器就是利用了閉包的原理,所以我們說裝飾器就是是閉包也是完全沒有問題的。

二、帶參數(shù)的裝飾器

我們可能看到過這樣的裝飾器

@decorator("arg","arg2")
def func():
    # do something
    pass

在一些代碼中我們發(fā)現(xiàn)有些裝飾器是有參數(shù)的,這樣可以帶參數(shù)的裝飾器是怎么實(shí)現(xiàn)的呢?下面我就和大家講一講帶參數(shù)的裝飾器是怎么實(shí)現(xiàn)的。

在了解了裝飾器的原理之后我們知道裝飾器其實(shí)就是一個(gè)返回一個(gè)函數(shù)的函數(shù)。

于是我們就想:既然有返回函數(shù)的函數(shù),那有沒有返回裝飾器的函數(shù)呢?當(dāng)然是有的!

事實(shí)上,上面提到的帶參數(shù)的裝飾器就是一個(gè)返回裝飾器的函數(shù)。

其中的原理也很簡(jiǎn)單,圓括號(hào)表示函數(shù)調(diào)用,而我們的程序都是從右到左執(zhí)行的,所以在程序運(yùn)行的時(shí)候 @ 后面的應(yīng)該是 decorator 返回的裝飾器。

說完原理,我們?cè)賮?lái)看看帶參數(shù)的裝飾器應(yīng)該怎么寫,先上代碼。

import time


def timeit(out="函數(shù)運(yùn)行時(shí)間為:%.2fs"):
    def decorator(func):
        def result():
            start_time = time.time()
            func()
            end_time = time.time()
            print(out % (end_time - start_time))
        return result
    return decorator

@timeit("函數(shù)運(yùn)行時(shí)間為:%.2fs  --來(lái)自帶參數(shù)的裝飾器")
def func():
    time.sleep(2)
    
func() # => 函數(shù)運(yùn)行時(shí)間為:2.00s  --來(lái)自帶參數(shù)的裝飾器

我們?cè)谠瓉?lái)打印函數(shù)運(yùn)行時(shí)間的裝飾器上添加了自定義打印語(yǔ)句的功能,在使用的時(shí)候和下面的代碼等價(jià)。

func = timeit("函數(shù)運(yùn)行時(shí)間為:%.2fs  --來(lái)自帶參數(shù)的裝飾器")(func)

帶參數(shù)的裝飾器也是利用了閉包將參數(shù)綁定到返回的函數(shù)上。

三、更加通用的裝飾器

前面兩部分講了裝飾器的原理,這一部分就講講要寫出一個(gè)通用的裝飾器需要注意的問題。

首先就是參數(shù)的問題,裝飾器返回的函數(shù)不是原來(lái)的函數(shù),函數(shù)的簽名也就和原來(lái)的函數(shù)簽名不一樣。

當(dāng)我們還是按照來(lái)的方式去調(diào)用函數(shù)就有可能會(huì)出問題,我們通過一個(gè)例子來(lái)看一下。

def decorator(func):
    def result(a):
        print(a)
        func()
    return result

@decorator
def func(a,b,c):
    print(a,b,c)
    
func(1, 2, 3)
# => TypeError: result() takes 1 positional argument but 3 were given

這里報(bào)錯(cuò)是因?yàn)檠b飾器返回的函數(shù)只接受一個(gè)參數(shù),我們還按照調(diào)用 func 的方式來(lái)調(diào)用 result當(dāng)然會(huì)報(bào)錯(cuò)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/44065.html

相關(guān)文章

  • 簡(jiǎn)單理解Python裝飾

    摘要:下面我們一起拋去無(wú)關(guān)概念,簡(jiǎn)單地理解下的裝飾器。用函數(shù)實(shí)現(xiàn)裝飾器裝飾器要求入?yún)⑹呛瘮?shù)對(duì)象,返回值是函數(shù)對(duì)象,嵌套函數(shù)完全能勝任。為了對(duì)調(diào)用方透明,裝飾器返回的對(duì)象要偽裝成被裝飾的函數(shù)。 來(lái)源:http://www.lightxue.com/under... ???????Python有大量強(qiáng)大又貼心的特性,如果要列個(gè)最受歡迎排行榜,那么裝飾器絕對(duì)會(huì)在其中。???????剛接觸裝飾器,會(huì)...

    Meils 評(píng)論0 收藏0
  • 詳解Python裝飾

    摘要:概括的講,裝飾器的作用就是為已經(jīng)存在的函數(shù)或?qū)ο筇砑宇~外的功能。在理解這些裝飾器之前,最好對(duì)函數(shù)的閉包和裝飾器的接口約定有一定了解。是一個(gè)非常簡(jiǎn)單的裝飾器加強(qiáng)包。 Python中的裝飾器是你進(jìn)入Python大門的一道坎,不管你跨不跨過去它都在那里。 為什么需要裝飾器 我們假設(shè)你的程序?qū)崿F(xiàn)了say_hello()和say_goodbye()兩個(gè)函數(shù)。 def say_hello(): ...

    DandJ 評(píng)論0 收藏0
  • 利用世界杯,讀懂 Python 裝飾

    摘要:今天就結(jié)合最近的世界杯帶大家理解下裝飾器。而德國(guó)是上屆的冠軍,又是這屆奪冠熱門。裝飾器的存在是為了適用兩個(gè)場(chǎng)景,一個(gè)是增強(qiáng)被裝飾函數(shù)的行為,另一個(gè)是代碼重用。在利用語(yǔ)法糖,簡(jiǎn)化賦值操作。行為良好的裝飾器可以重用,以減少代碼量。 Python 裝飾器是在面試過程高頻被問到的問題,裝飾器也是一個(gè)非常好用的特性,熟練掌握裝飾器會(huì)讓你的編程思路更加寬廣,程序也更加 pythonic。 show...

    xiguadada 評(píng)論0 收藏0
  • Python】一文弄懂python裝飾器(附源碼例子)

    摘要:裝飾器的使用符合了面向?qū)ο缶幊痰拈_放封閉原則。三簡(jiǎn)單的裝飾器基于上面的函數(shù)執(zhí)行時(shí)間的需求,我們就手寫一個(gè)簡(jiǎn)單的裝飾器進(jìn)行實(shí)現(xiàn)。函數(shù)體就是要實(shí)現(xiàn)裝飾器的內(nèi)容。類裝飾器的實(shí)現(xiàn)是調(diào)用了類里面的函數(shù)。類裝飾器的寫法比我們裝飾器函數(shù)的寫法更加簡(jiǎn)單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...

    liuchengxu 評(píng)論0 收藏0
  • Python 裝飾使用指南

    摘要:裝飾器是可調(diào)用的對(duì)象,其參數(shù)是另一個(gè)函數(shù)被裝飾的函數(shù)。第二大特性是,裝飾器在加載模塊時(shí)立即執(zhí)行。另一個(gè)常見的裝飾器是,它的作用是協(xié)助構(gòu)建行為良好的裝飾器。 裝飾器是可調(diào)用的對(duì)象,其參數(shù)是另一個(gè)函數(shù)(被裝飾的函數(shù))。 裝飾器基礎(chǔ)知識(shí) 首先看一下這段代碼 def deco(fn): print I am %s! % fn.__name__ @deco def func(): ...

    NeverSayNever 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<