鐵樂學Python_day12_裝飾器

来源:https://www.cnblogs.com/tielemao/archive/2018/04/04/8719122.html
-Advertisement-
Play Games

裝飾器本質上就是一個python函數,他可以讓其他函數在不需要做任何代碼變動的前提下,增加額外的功能,裝飾器的返回值也是一個函數對象。 裝飾器的應用場景:比如插入日誌,性能測試,事務處理,緩存等等場景。 ...


【函數的有用信息】

例:
def login(user, pwd):
    '''
    功能:登錄調用
    參數:分別有user和pwd,作用分別是用戶和密碼;
    return: 返回值是登錄成功與否(True,False)
    '''
    print(user, pwd, "歡迎登錄")

print(login.__name__) #查看函數名字
print(login.__doc__)  #查看函數註釋

login

    功能:登錄調用
    參數:分別有user和pwd,作用分別是用戶和密碼;
    return: 返回值是登錄成功與否(True,False)

函數名.__name__ 這是查看函數名字的方法
函數名.__doc__ 這是查看函數註釋的方法

上面的兩個方法在做運維審計日誌時很有用,配合裝飾器使用可以記錄下不少信息。
不過要是用在被裝飾器裝飾過的函數時顯示的會變成是裝飾器的函數名和註釋,
所以需要用到wraps模塊去實現顯示的名字和註釋仍是被裝飾的原函數。

例:

from functools import wraps
# 函數工具庫的warps模塊來實現裝飾器函數名字仍是被裝飾的原函數名字

def deco(func):
    @wraps(func) #加在最內層函數正上方
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@deco
def index():
    '''首頁的註釋'''
    print('from index')

print(index.__doc__)
print(index.__name__)

首頁的註釋
index

【裝飾器Decorator】

裝飾器本質上就是一個python函數,他可以讓其他函數在不需要做任何代碼變動的前提下,增加額外的功能,裝飾器的返回值也是一個函數對象。
裝飾器的應用場景:比如插入日誌,性能測試,事務處理,緩存等等場景。

裝飾器的語法糖:@緊接著裝飾器的函數名,放置在需被裝飾的函數上方。
因為@的形象很像一個甜甜的一個一個圈的棒棒糖,所以又被人們愛稱為語法糖。
相對直接生硬的將要被裝飾的函數名賦值成裝飾器的函數名這種方式來說靈活和優雅得多,
就算要改動也不致於太麻煩。

例:下麵是一個在調用函數時先進行列印格林威治時間的Decorator:

import time  # 引入時間模塊

def wrapper(func):
    '''
    此裝飾器主要用於測試被裝飾函數的工作時間
    '''
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        time.sleep(0.2322) # 模擬函數執行後經過的時間
          end_time = time.time()
        sec = end_time - start_time
        print('此程式執行時間為%s' % round(sec, 5))
        return ret # 相當於是返回func()
    return inner

@wrapper # 語法糖裝飾上後,相當於是將下方的 hello = wrapper(hello)
def hello():
    print('hero say hello')

hello() 
# 相當於是執行wrapper(hello)(),就相當於是執行wrapper裡層的inner函數體。
# 所以會執行額外添加的功能,同時函數也正常調用了。

# 執行效果如下:
hero say hello
此程式執行時間為0.23301


上面例子是嵌套的裝飾器,它比較簡單。
因為我們在內層的inner函數中有設置動態參數(*args和**kwargs)
被裝飾的函數有設置參數的時候,也可以正常傳遞,如:
@wrapper # 語法糖裝飾上後,相當於是將下方的 hello = wrapper(hello)
def hello(name):
    print('hero say hello %s' % name)

hello('tiele')
# 相當於是執行wrapper(hello)(),就相當於是執行wrapper裡層的inner函數體。
# 所以會執行額外添加的功能,同時函數也正常調用了。

返回:
hero say hello tiele
此程式執行時間為0.23301


但有一點要註意的
經過decorator裝飾之後的函數,它們的__name__已經從原來的'函數名'變成了'inner'print(hello.__name__)

hero say hello tiele
此程式執行時間為0.23301
inner

因為直接用查看函數名字與註釋的方法去查看經過被裝飾器裝飾了的函數會得出的是錯誤的信息(返回的是裝飾器的名字與註釋),這不符合我們的需求,(雖然直接在inner函數體內列印func.__name__可以得出原函數名,但在全局環境下使用.__name__查看還是會出錯)所以還得用上python內置的functools.wraps去支持全局查看函數名時能看到正確的原函數名:

import time  # 引入時間模塊
import functools # 引入函數工具模塊

def wrapper(func):
    '''
    此裝飾器主要用於測試被裝飾函數的工作時間
    '''
    # 加在最內層函數的正上方,因為要的是最內函數的名字
    @functools.wraps(func)
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        time.sleep(0.2322) # 模擬函數執行後經過的時間
        end_time = time.time()
        sec = end_time - start_time
        print('此程式執行時間為%s' % round(sec, 5))
        return ret # 相當於是返回func()
    return inner

@wrapper # 語法糖裝飾上後,相當於是將下方的 hello = wrapper(hello)
def hello(name):
    print('hero say hello %s' % name)

hello('tiele')
# 相當於是執行wrapper(hello)(),就相當於是執行wrapper裡層的inner函數體。
# 所以會執行額外添加的功能,同時函數也正常調用了。

print('函數名為'+ hello.__name__)

hero say hello tiele
此程式執行時間為0.23301
函數名為hello

如上例,看起來像是在inner函數上方也裝了一個語法糖裝飾器一樣。

【帶參數的裝飾器】

函數中帶參數,我們是實現了。可是,有些情況需要裝飾器本身也帶上參數以便控制流程和裝飾器開關的,就很有必要再嵌套一層外面的函數去實現了。

例:(最外層這次參數內是動態參數,而不是接受被裝飾的函數名做參數了)

from functools import wraps
# 函數工具庫的warps模塊來實現裝飾器函數名字仍是被裝飾的原函數名字
import time
# 引進時間模塊

def timer(*args, **kwargs):
    def wrapper(f):
        @wraps(f)
        # 加在最內層函數的正上方,因為要的是最內函數的名字
        def inner(*args, **kwargs):
            '''
            此功能用於測試函數起始時間與結束時間
            :param args:
            :param kwargs:
            :return:
            '''
            if flag:
                StartTime = time.time()
                ret = f(*args, **kwargs)
                time.sleep(0.3)
                EndTime = time.time()
                sec = EndTime - StartTime
                print('此程式執行效率%f' % sec)
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    return wrapper

flag = True
@timer()
def hello_word():
    '''
    向世界say hello
    :return: None
    '''
    print('hello,word.hello,hero.')

hello_word()
print(hello_word.__name__)
print(hello_word.__doc__)

hello,word.hello,hero.
此程式執行效率0.300017
hello_word

    向世界say hello
    :return: None



flag為False,裝飾器的計算時間功能便不會執行。

def timer(flag):
    # 帶flag參數,可以控制裝飾器的開關
    def wrapper(f):
        @wraps(f)
        # 加在最內層函數的正上方,因為要的是最內函數的名字
        def inner(*args, **kwargs):
            '''
            此功能用於測試函數起始時間與結束時間
            :param args:
            :param kwargs:
            :return:
            '''
            if flag:
                StartTime = time.time()
                ret = f(*args, **kwargs)
                time.sleep(0.3)
                EndTime = time.time()
                sec = EndTime - StartTime
                print('此程式執行效率%f' % sec)
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    return wrapper

@timer(False)
def hello_word():
    '''
    向世界say hello
    :return: None
    '''
    print('hello,word.hello,hero.')

hello_word()

hello,word.hello,hero.

上面例子中,當我們將flag的值改成False,便不會觸發裝飾器的額外功能。

【開放封閉原則】

1.對擴展是開放的
  為什麼要對擴展開放呢?
  任何一個程式,不可能在設計之初就已經想好了所有的功能並且未來不做任何更新和修改。
所以必須允許代碼擴展、添加新功能。

2.對修改是封閉的
  為什麼要對修改封閉呢?
  寫的一個函數,很有可能已經交付給其他人使用了,如果這個時候對其進行了修改,很有可能影響其他已經在使用該函數的用戶。

裝飾器完美的遵循了這個開放封閉原則。

【多個裝飾器裝飾一個函數】

例:

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()

效果:

wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func


規律就是先執行完原里函數前面的語句,再執行原函數,再到里函數後面的語句。
如圖:

end
2018-4-4


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 策略模式將不同演算法的邏輯抽象介面封裝到一個類中,通過組合和多態結合的方式來進行不同演算法具體的實現。 作用 策略模式是一種定義一系列演算法的方法,Strategy類層次為Context定義了一系列的可重用的演算法或行為, 所有的演算法以相同的方式進行調用,減少了演算法類之間的耦合 類視圖 實現 Strateg ...
  • 面向對象這門課,寫了3次作業,在這裡分享一下自己的設計思路、分析總結等,歡迎一起探討~ ...
  • 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 一. 下載並安裝Nginx 去Nginx官網下載 我這裡選取nginx/Windows-1.10.3版本,下載後解壓出來即可,解壓出來的路徑不能含有中文 我解壓後將其放置的路徑如下 二、開始運行 在當前目錄下按住shift+滑鼠右鍵,選擇“在此處 ...
  • 這篇文章主要介紹了python sort、sorted高級排序技巧,本文講解了基礎排序、升序和降序、排序的穩定性和複雜排序、cmp函數排序法等內容,需要的朋友可以參考下 Python list內置sort()方法用來排序,也可以用python內置的全局sorted()方法來對可迭代的序列排序生成新的 ...
  • ...
  • 目前知道的迭代器有兩種: 一種是調用方法直接返回的; 一種是可迭代對象通過執行iter方法得到的。 迭代器的好處是可以節省記憶體。 如果在某些情況下,我們也需要節省記憶體,就只能自己寫。自己寫的這個能實現迭代器功能的東西就叫生成器。 ...
  • 1.寫函數,返回一個撲克牌列表,裡面有52項,每一項是一個元組 例如:[(‘紅心’,2),(‘草花’,2), …(‘黑桃’,‘A’)] 2.寫函數,傳入n個數,返回字典{‘max’:最大值,’min’:最小值} 例如:min_max(2,5,7,8,4) 返回:{‘max’:8,’min’:2} 3 ...
  • 常用模塊 1 logging模塊 日誌級別:Noset (不設置) Debug (調試信息) 也可用10表示 Info--(消息信息) 也可用20表示 Warning (警告信息) 也可用30表示 Error (錯誤消息) 也可用40表示 Critical (嚴重錯誤) 也可用50表示 預設級別是W ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...