python裝飾器入門探究

簡介

裝飾器是可調用的對象,其參數是另外一個函數(被裝飾的函數)。 python的世界中,一切皆對象,把函數看作一個對象,一個能夠拼接、編輯的對象。執行對象的方法是__ 對象()__python

裝飾器只是python的語法糖

觀察下面的兩個調用,他們的效果是同樣的。flask

#!/usr/bin/env python
#-*-coding:utf-8-*-

''' @file: dec4.py @time: 2018/12/13 17:15 '''
import functools

def register(func):        
 @functools.wraps(func)
    def warpper(*args,**kwargs):
        print("啦啦啦,這裏裝飾函數 %s"%func.__name__)
        result = func()      #這裏就是真正的執行邏輯
        return result
    return warpper       # 返回包裝後的函數

@register
def f1():
    print("使用@語法的裝飾器 %s"%f1.__name__)


def f2():
    print("沒@語法的裝飾器 %s" % f2.__name__)

if __name__ == '__main__':
    f1()
    register(f2)()
    #或者
    # f_ = register(f2)
    # f_()
複製代碼

image.png | center | 572x147

裝飾器啓動裝飾的時機

一、文件導入時裝飾器已經啓動對函數的裝飾。

裝飾器的一個關鍵特性是,它們在被裝飾的函數定義以後當即運行。 這一般是在導入時(即 Python 加載模塊時)。--cookbookapp

建立文件 dec1.py框架

# dec1.py

registry = []  # 存放註冊函數

def register(func):
    print('裝飾器入列表(%s)' % func.__name__)
    registry.append(func)
    return func

@register
def f1():
    print('執行 %s'%f1.__name__)

@register
def f2():
    print('執行 %s' % f2.__name__)
複製代碼

建立文件 dec2.py函數

# dec2.py
from 你的目錄 import dec1
複製代碼

執行文件 dec2.py 輸出以下:spa

image.png | center | 539x87

裝飾器返回的結果

一、返回目標函數。 二、返回目標函數的結果。日誌

以上 register的用法適用於對函數進行預處理的場景,例如flask框架的路由註冊,在flask app啓動時將全部的函數收集起來,與註冊的路徑進行一一對應,可是函數運行時跟原函數沒有任何區別,由於裝飾器返回的仍是目標函數。假如咱們須要在函數運行時進行日誌的輸出,則須要將函數進行一層封裝,即將目標函數包裝成另外一個函數,這時返回的是另外一個函數,只是包裝後的函數能夠進行額外的操做。code

如下是兩個裝飾器的寫法,注意區別。cdn

import functools
def register1(func):      #在文件引入時返回目標函數的裝飾器
    #這裏進行一些函數預處理的操做,例如將函數名字收集進一個列表
    #list.append(func.__name__)
    return func

def register2(func):     #在函數運行時返回函數的裝飾器
 @functools.wraps(func)
    def warpper(*args,**kwargs):
        print("函數沒有執行 %s"%func.__name__)
        result = func      #這裏就是真正的執行邏輯 #注意-----這裏沒有執行函數
        return result
    return warpper       # 返回包裝後的函數


def register3(func):     #返回目標函數結果的裝飾器
 @functools.wraps(func)
    def warpper(*args,**kwargs):

        print("函數開始執行 %s"%func.__name__)
        result = func()      #這裏就是真正的執行邏輯
        return result
    return warpper       # 返回包裝後的函數

@register1
def f1():
    print("執行函數 %s"%f1.__name__)

@register2
def f2():
    print("執行函數 %s"%f2.__name__)

@register3
def f3():
    print("執行函數 %s"%f3.__name__)


if __name__ == '__main__':
    f1()
    f2()
    f3()
複製代碼

執行結果:對象

image.png | center | 547x165

這時添加代碼:

if __name__ == '__main__':
    f1()
    f2()
    f_ = f2()
    print("開始真正執行f2函數啦")
    f_()
    f3()
複製代碼

執行結果:

image.png | center | 575x225

裝飾函數的屬性保持

一、使用@functools.wraps

你會發現下面的代碼中,若是wrapper函數沒有使用@functools.wraps裝飾的話,f1.__name__返回的是wrapper函數的名字,這是由於咱們返回的函數已是wrapper這個函數,因此__name__屬性也就不是原來函數的了,@functools.wraps裝飾器的做用就是將目標函數的屬性例如__name__等原封不動轉移給包裝好的函數。

#!/usr/bin/env python
#-*-coding:utf-8-*-

''' @file: dec4.py @time: 2018/12/13 17:15 '''
import functools

def register(func):     #返回目標函數結果的裝飾器

    def warpper(*args,**kwargs):
        result = func()      #這裏就是真正的執行邏輯
        return result
    return warpper       # 返回包裝後的函數

def register2(func):     #返回目標函數結果的裝飾器
 @functools.wraps(func)
    def warpper(*args,**kwargs):
       
        result = func()      #這裏就是真正的執行邏輯
        return result
    return warpper       # 返回包裝後的函數

@register
def f1():
    print("函數名爲 %s"%f1.__name__)

@register2
def f2():
    print("函數名爲 %s" % f2.__name__)


if __name__ == '__main__':
    f1()
    f2()
    #或者
    #f_ = register(f2)
    #f_()
複製代碼

執行結果:

帶參數的裝飾器

一、外層嵌套一層函數傳參,返回一個裝飾器。

裝飾器也是一個對象,只要使用一個函數封裝裝飾器,返回目標裝飾器便可,這時就能夠經過外層的函數傳進參數。

#!/usr/bin/env python
# -*-coding:utf-8-*-

''' @file: dec5.py @time: 2018/12/13 18:13 '''
import functools


def dec_arg(arg=None):

    def register(func):
 @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('傳進的參數爲 %s' % arg)
            return func(*args, **kwargs)
        return wrapper
    return register  #這是裝飾器

@dec_arg(arg='aaaa')
def f1():
    print("執行 %s"%f1.__name__)

def f2():
    print("執行 %s" % f2.__name__)


if __name__ == '__main__':
    f1()

    f2_ = dec_arg(arg='bbbb')(f2)
    f2_()

    dec_arg(arg='cccc')(f2)()
複製代碼

image.png | left | 652x240
相關文章
相關標籤/搜索