亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

python學習筆記 函數(shù)裝飾器

jsliang / 591人閱讀

摘要:實現(xiàn)一個簡單的裝飾器輸出被裝飾函數(shù)的運行時間簡單運用運行結(jié)果運行過程中,首先輸出裝飾器函數(shù)中的內(nèi)容被裝飾函數(shù)運行時間長度函數(shù)名稱和實際參數(shù)計算結(jié)果然后得到最終的計算結(jié)果。

函數(shù)裝飾器

函數(shù)裝飾器用于在源碼中“標記”函數(shù), 以某種方式增強函數(shù)的行為,這是一個強大的功能。

函數(shù)裝飾器是一個可調(diào)用對象,其參數(shù)是另外一個函數(shù),即被裝飾函數(shù)。裝飾器可能處理被裝飾函數(shù),然后將其返回,或者將其替換成另一個函數(shù)或可調(diào)用對象。

函數(shù)裝飾器的一個重要特性就是,它們在被裝飾的函數(shù)定義之后立即運行。

registry = []

def register(func):
    print("running register(%s)" % func)
    registry.append(func)
    return func

@register
def f1():
    print("running f1()")

@register
def f2():
    print("running f2()")

def f3():
    print("running f3()")

def main():
    print("running main()")
    print("registry ->", registry)
    f1()
    f2()
    f3()

if __name__ == "__main__":
    main()

運行結(jié)果:

running register()
running register()
running main()
registry -> [, ]
running f1()
running f2()
running f3()

可以看到,被裝飾的 f1() 和 f2() 首先被運行,隨后才運行main()中的語句。

被裝飾函數(shù)運行時,其本身的內(nèi)容(示例中print語句)并沒有被執(zhí)行,而是運行了裝飾器函數(shù)中的print語句;這就是裝飾器的作用,替代被裝飾函數(shù),同時裝飾器也可以調(diào)用外界自由變量(registry),從而引出一個重要概念:

閉包

實例中registry變量和register函數(shù)組合的共同體,被成稱為閉包。

該例中有兩個不太尋常的地方:

裝飾器函數(shù)和被裝飾函數(shù)定義在一個模塊之中;一般來說,兩者應該定義在不同的模塊;

register裝飾器返回的函數(shù)與通過參數(shù)傳入的相同;實際上,大部分裝飾器會在內(nèi)部定義一個函數(shù),然后將其返回。

實現(xiàn)一個簡單的裝飾器
# clockdeco.py 輸出被裝飾函數(shù)的運行時間
import time


def clock(func):
    def clocked(*args):
        t0 = time.perf_counter()
        result = func(*args)
        elapsed = time.perf_counter() - t0
        name = func.__name__
        arg_str = ",".join(repr(arg) for arg in args)
        print("[%0.8fs] %s(%s) -> %r" % (elapsed, name, arg_str, result))
        return result
    return clocked

簡單運用:

# clockdeco_demo.py 
import time
from clockdeco import clock

@clock
def snooze(seconds):
    time.sleep(seconds)

@clock
def factorial(n):
    return 1 if n < 2 else n*factorial(n-1)

def main():
    print("*" * 40, "Calling snooze(.123)")
    snooze(.123)
    print("*" * 40, "Calling factorial(6)")
    print("6! =", factorial(6))

if __name__ == "__main__":
    main()

運行結(jié)果:

**************************************** Calling snooze(.123)
[0.12240868s] snooze(0.123) -> None
**************************************** Calling factorial(6)
[0.00000068s] factorial(1) -> 1
[0.00020317s] factorial(2) -> 2
[0.00039755s] factorial(3) -> 6
[0.00053638s] factorial(4) -> 24
[0.00062375s] factorial(5) -> 120
[0.00067319s] factorial(6) -> 720
6! = 720

運行過程中,首先輸出裝飾器函數(shù)中的內(nèi)容:

被裝飾函數(shù)運行時間長度;

函數(shù)名稱和實際參數(shù)

計算結(jié)果

然后得到最終的計算結(jié)果??梢?,裝飾器函數(shù)的優(yōu)先級較高

當然,該實例中的裝飾器具有幾個缺點:

不支持關(guān)鍵字參數(shù)

遮蓋了被裝飾函數(shù)的__name__和__doc__屬性

下面的例子對其做出改進:

# clockdeco2.py
import time
import functools


def clock(func):
    @functools.wraps(func)
    def clocked(*args, **kwargs):
        t0 = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - t0
        name = func.__name__
        arg_lst = []
        if args:
            arg_lst.append(",".join(repr(arg) for arg in args))
        if kwargs:
            pairs = ["%s=%r" % (k, w) for k, w in sorted(kwargs.items())]
            arg_lst.append(", ".join(pairs))
        arg_str = ", ".join(arg_lst)
        print("[%0.8fs] %s(%s) -> %r " % (elapsed, name, arg_str, result))
        return result
    return clocked

運用:

# clockdeco_demo.py
import time
from clockdeco2 import clock

@clock
def snooze(seconds):
    time.sleep(seconds)

@clock
def factorial(n):
    return 1 if n < 2 else n*factorial(n-1)

def main():
    print("*" * 40, "Calling snooze(.123)")
    snooze(.123)
    print("*" * 40, "Calling factorial(6)")
    print("6! =", factorial(6))

if __name__ == "__main__":
    main()

運行結(jié)果:

**************************************** Calling snooze(.123)
[0.12328553s] snooze(0.123) -> None
**************************************** Calling factorial(6)
[0.00000000s] factorial(1) -> 1
[0.00000000s] factorial(2) -> 2
[0.00000000s] factorial(3) -> 6
[0.00099683s] factorial(4) -> 24
[0.00099683s] factorial(5) -> 120
[0.00099683s] factorial(6) -> 720
6! = 720

改進后的clockdeco2.py中,使用functools.wraps裝飾器把相關(guān)屬性從func復制到clocked中,此外,這個新版本還能正確處理關(guān)鍵字參數(shù)。functools.wraps是標準庫中的裝飾器,它可以用于裝飾一個函數(shù),但是對于被裝飾函數(shù)本身的功能沒有任何影響,它的功能只是傳遞函數(shù)內(nèi)置參數(shù)。

參數(shù)化clock裝飾器
# clockdeco_param.py
import time


DEFAULT_FMT = "[{elapsed:0.8f}s] {name}({args}) -> {result}"

def clock(fmt=DEFAULT_FMT):
    def decorate(func):
        def clocked(*_args):
            t0 = time.time()
            _result = func(*_args)
            elapsed = time.time() - t0
            name = func.__name__
            args = ",".join(repr(arg) for arg in _args)
            result = repr(_result)
            print(fmt.format(**locals()))
            return _result
        return clocked
    return decorate

def clock 是參數(shù)化裝飾器工廠函數(shù)

decorate(func) 是真正的裝飾器

clocked 包裝被裝飾的函數(shù)

示例1:

# clockdeco_param_demo.py
import time
from clockdeco_param import clock

@clock()
def snooze(seconds):
    time.sleep(seconds)

for i in range(3):
    snooze(.123)

運行結(jié)果:

[0.12367034s] snooze(0.123) -> None
[0.12367010s] snooze(0.123) -> None
[0.12366986s] snooze(0.123) -> None

示例2:

# clockdeco_param_demo.py
import time
from clockdeco_param import clock

@clock("{name}:{elapsed}s")
def snooze(seconds):
    time.sleep(seconds)

for i in range(3):
    snooze(.123)

運行結(jié)果:

snooze:0.12366843223571777s
snooze:0.12369871139526367s
snooze:0.12366509437561035s

示例3:

# clockdeco_param_demo.py
import time
from clockdeco_param import clock

@clock("{name}({args}) dt={elapsed:0.3f}s")
def snooze(seconds):
    time.sleep(seconds)

for i in range(3):
    snooze(.123)

運行結(jié)果:

snooze(0.123) dt=0.124s
snooze(0.123) dt=0.124s
snooze(0.123) dt=0.124s

分析三個示例可以看出,當裝飾器clock的參數(shù)不同時,被裝飾函數(shù)運行所得結(jié)果也會不同。

python中參數(shù)化裝飾器的用意在于將更多的參數(shù)傳送給裝飾器,因為裝飾器的第一個參數(shù)一定是被裝飾函數(shù)。

備忘裝飾器 functools.lru_cache

functools.lru_cache和functools.wraps一樣,也是一個python內(nèi)置裝飾器,它的功能是將耗時的函數(shù)結(jié)果保存起來,避免傳圖相同的參數(shù)造成重復計算,從而節(jié)省代碼運行時間。

下面以斐波那契數(shù)列寫一個案例:

使用functools.lru_cache

import functools
from clockdeco import clock

@functools.lru_cache()
@clock
def fibonacci(n):

if n < 2:
    return  n
return fibonacci(n-2) + fibonacci(n-1)

if __name__=="__main__":

print(fibonacci(30))

運行結(jié)果:

[0.00000000s] fibonacci(0) -> 0
[0.00000068s] fibonacci(1) -> 1
......
[0.00000271s] fibonacci(29) -> 514229
[0.00542815s] fibonacci(30) -> 832040
832040

多次運行,計算fibonacci(30)大概耗時0.005秒左右

作為對比:

import functools
from clockdeco import clock


@clock
def fibonacci(n):
    if n < 2:
        return  n
    return fibonacci(n-2) + fibonacci(n-1)

if __name__=="__main__":
    print(fibonacci(30))

運行結(jié)果:

.......
[156.42139917s] fibonacci(28) -> 317811
[230.80184171s] fibonacci(29) -> 514229
[368.52227404s] fibonacci(30) -> 832040
832040

嗯……陷入沉思,雖然筆記本渣渣配置,但是運行了6分鐘,差距太大

總結(jié)

函數(shù)裝飾器就是用來裝飾函數(shù)的函數(shù),并使用內(nèi)置函數(shù)替代被裝飾函數(shù)

被裝飾函數(shù)在定義時就運行,無需顯示調(diào)用

內(nèi)置裝飾器functools.wraps可以為被裝飾函數(shù)賦值內(nèi)置屬性

參數(shù)化裝飾器可以接受不同參數(shù)并適用于被裝飾函數(shù),顯示不同的作用,相當于裝飾器中的定制化

參數(shù)化的裝飾器嵌套層次較深,邏輯相對復雜

內(nèi)置裝飾器functools.lru_cache采用緩存機制,存儲需要多次調(diào)用的函數(shù)值,從而降低代碼運行時間

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

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/42259.html

相關(guān)文章

  • Python裝飾學習筆記

    摘要:前言最近跟著流暢的和學習,看到裝飾器部分,有些頭大倒不是因為概念難以理解,而是書和網(wǎng)上文章中有些地方有些矛盾之處在簡單學習和實踐之后,整理出我對裝飾器的理解如下裝飾器的定義在不同語境下,裝飾器有不一樣的含義,我大致認為有種定義一種把另一個對 前言 最近跟著《流暢的Python》和《Python Cookbook》學習,看到裝飾器部分,有些頭大倒不是因為概念難以理解,而是書和網(wǎng)上文章中有...

    Cristalven 評論0 收藏0
  • python學習筆記裝飾

    摘要:裝飾器介紹中的裝飾器的目的是為一個目標函數(shù)添加額外的功能卻不修改函數(shù)本身。裝飾器的本身其實是一個特殊的函數(shù)。那么有啥更好的解決方式呢裝飾器代碼像上面這么寫,可以較好地解決了上面提到的第一個問題。裝飾器語法糖放在函數(shù)前面,相當于執(zhí)行了等。 怎么理解python中的裝飾器 一個比喻 知乎上有一個比較形象的比喻 https://www.zhihu.com/questio...:人類穿著內(nèi)褲很...

    張金寶 評論0 收藏0
  • Python 裝飾的理解

    摘要:的裝飾器可以實現(xiàn)在代碼運行期間修改函數(shù)的上下文,即可以定義函數(shù)在執(zhí)行之前進行何種操作和函數(shù)執(zhí)行后進行何種操作,而函數(shù)本身并沒有任何的改變。中的參數(shù),實際上則是傳遞給實際上是的參數(shù)因為裝飾器也是個函數(shù),那么裝飾器自己的能不能有參數(shù)傳遞呢。 Python的裝飾器可以實現(xiàn)在代碼運行期間修改函數(shù)的上下文, 即可以定義函數(shù)在執(zhí)行之前進行何種操作和函數(shù)執(zhí)行后進行何種操作, 而函數(shù)本身并沒有任何的改...

    animabear 評論0 收藏0
  • flask文檔學習筆記1-快速入門

    摘要:示例如下靜態(tài)路由使用動態(tài)變量的路由未指定變量類型使用動態(tài)變量的路由指定變量類型指定的路由變量,可以作為被裝飾的函數(shù)參數(shù)傳入進來。 開始決定認真的在網(wǎng)上寫一些東西,主要原因還是在于希望能提升學習效果。雖說python寫了有幾年,但是web后端框架的確沒怎么接觸過,買了本狗書寥寥草草的過了一遍,發(fā)現(xiàn)很多東西還是理解不深,真的是好記性不如爛筆頭,知識也要從基礎(chǔ)開始,退回來好好看看官方文檔,再...

    lingdududu 評論0 收藏0
  • Python

    摘要:最近看前端都展開了幾場而我大知乎最熱語言還沒有相關(guān)。有關(guān)書籍的介紹,大部分截取自是官方介紹。但從開始,標準庫為我們提供了模塊,它提供了和兩個類,實現(xiàn)了對和的進一步抽象,對編寫線程池進程池提供了直接的支持。 《流暢的python》閱讀筆記 《流暢的python》是一本適合python進階的書, 里面介紹的基本都是高級的python用法. 對于初學python的人來說, 基礎(chǔ)大概也就夠用了...

    dailybird 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<