1. 程式人生 > >Python 裝飾器初探

Python 裝飾器初探

Python 裝飾器初探

在談及Python的時候,裝飾器一直就是道繞不過去的坎。面試的時候,也經常會被問及裝飾器的相關知識。總感覺自己的理解很淺顯,不夠深刻。是時候做出改變,對Python的裝飾器做個全面的瞭解了。

1. 函式裝飾器

直接上程式碼,看看裝飾器到底幹了些什麼?

from functools import wraps
import time


def time_cost(func):
    @wraps(func)
    def f(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
    return f


@time_cost
def test(*args, **kwargs):
    time.sleep(1.0)


if __name__ == "__main__":
    test()

上面的Python程式碼,執行後,會給出test函式的執行時間。程式碼的執行順序大概如下,首先是將test作為值傳遞給time_cost函式,返回函式f,然後再呼叫f,這是帶有time_cost裝飾器的test函式的大致執行過程。

從中,不難看出,即使不使用裝飾器符號,我們利用Python的語言特性,也能達成上述目的。用裝飾器符號的好處是簡化了程式碼,增加了程式碼的可讀性。

這是一段非常簡單的對函式使用裝飾器的Python程式碼。等等,@wraps(func)是什麼鬼?悄悄幹了什麼哇?

我們稍微修改下上式的程式碼,結果如下:

from functools import wraps
import time


def time_cost(func):
    def f(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)

    print('hello world')
    return f


@time_cost
def test(*args, **kwargs):
    time.sleep(1.0)


if __name__ == "__main__":
    print(test.__name__)

發現輸出了hello world,同時輸出test.__name__,居然變成了f,並不是我們預期的test。根據這樣的輸出結果,我們不難得出,其實被裝飾器time_cost修飾過的函式test本質上已經等同於time_cost(test),此時訪問test.__name__實際上訪問的是time_cost(test).__name__,得到的當然就是f啦。當我們加上@wraps(func),此時test.__name__變成了test