Python 裝飾器

裝飾器


在加強原函數的功能的同時,不修改原函數的定義,這種在代碼運行期間動態增長功能的方式,就稱爲裝飾器(Decorator)。裝飾器,本質是一個返回函數的高階函數。在瞭解裝飾器以前,也簡單介紹下返回函數的相關內容。python

返回函數


以前,咱們講太高階函數,map()filter() 函數等高階函數,可以接受函數做爲參數,而函數一樣也能夠做爲結果值返回。微信

先定義一個簡單的函數:閉包

>>> def func():    
...     print("return function")

上述代碼只是普通的一個函數。如果,不須要當即輸出,能夠返回函數,而不是直接輸出:app

>>> def return_func():
...     def func():
...         print("return function")
...     return func

當調用 return_func() 時,返回的不是輸出結果,而是函數:函數

>>> f = return_func() 
>>> f
<function return_func.<locals>.func at 0x000002C03AF091E0>

當調用 f 時,才輸出內容:日誌

>>> f()
return function

這是返回函數的簡單應用。code

閉包


閉包,是指在一個函數內部定義了另外一個函數,而內部函數引用了外部函數的參數和局部變量,當返回內部函數時,相關參數和變量存儲在返回的函數中。orm

下面代碼實現一個閉包的操做:get

>>> def lazy_sum(*args):
...     def sum():
...         num = 0 
...         for x in args:
...             num += x 
...         return num
...     return sum
...

這個例子中,內部函數調用了外部函數的參數 args,當調用 lazy_sum 時,返回的函數存儲着相關參數和變量。只有當再次調用返回函數,纔會得出運算結果。io

>>> f = lazy_sum(1,2,3,4,5)
>>> f
<function lazy_sum.<locals>.sum at 0x000002C03AF31488>
>>> f()
15

這裏須要注意,每次調用外部函數,返回的函數都是新的函數,即便傳入的參數都相同:

>>> f1 = lazy_sum(1,2,3,4,5) 
>>> f2 = lazy_sum(1,2,3,4,5) 
>>> f1 is f2
False
>>> f1 == f2 
False

該例子中,f1()f2() 的結果互不影響。

還有個須要注意的地方,返回函數不是馬上執行,而是調用了 f() 才執行。嘗試用另一個例子說明這種狀況,示例以下:

>>> def count():
...     lst = []
...     for i in range(1, 4):
...         def func():
...             return i * i
...         lst.append(func)
...     return func
...
>>> f1, f2, f3 = count()

在這個例子中,每次循環,都建立一個新的函數,將建立的 3 個函數返回。

這裏的結果,可能會猜想調用 f1(), f2(), f3() 結果分別是 1, 4, 9,但實際結果卻都是 9

>>> f1()
9
>>> f2()
9
>>> f3()
9

這裏是由於返回的函數引用了變量 i,可是沒有馬上執行。等 3 個函數都返回時,引用的變量 i 的值已經所有變成了 3,因此最終結果是 9

因此,返回閉包時,返回函數不要引用循環變量,或者後續會發生變化的值。

裝飾器


前面已經說明,裝飾器,本質上是一個返回函數的高階函數。嘗試用例子說明,

>>> def log(func):                      
...     def warpper(*args, **kw):       
...         print('call {}():'.format(func.__name__)
...         return func(*args, **kw)
...     return wrapper

上面的 log 是一個裝飾器,接受函數做爲參數,返回函數。藉助 Python 的 @ 語法,把裝飾器置於函數的定義處:

@log
def func():
    print("function name")

調用 func() 函數,會運行 func() 自己函數,還會在運行 func() 前,打印一行日誌:

>>> func()
call func():
function name

在這裏,將 @log 放到 func() 函數的定義處,至關於下列語句:

func = log(func)

因爲 log() 是裝飾器,返回一個函數。可是,原來的 func() 還存在,只是如今同名的 func() 指向了新的函數,因此調用的 func() 時,執行的將是 wrapper() 函數。在 wrapper() 函數內,首先先打印日誌,而後再調用原始函數。

這些就是裝飾器的一些內容,至於更深刻的部分,後續會繼續更新介紹。


以上就是本篇的主要內容

題外話: 近期網上散佈不少的謠言,致使不少人沒法正確識別真假。下面的連接,是騰訊新聞的一個平臺【較真】,實時給你們闢謠以及科普,能夠幫助你們分清真相。儘可能作到不傳謠不信謠。

https://vp.fact.qq.com/home

歡迎關注微信公衆號《書所集錄》
相關文章
相關標籤/搜索