python-裝飾器

来源:https://www.cnblogs.com/saury/archive/2022/09/28/16739191.html
-Advertisement-
Play Games

裝飾器 (1)什麼是裝飾器: 器指的是工具,可以定義成函數 裝飾指的是為其他事務添加額外的東西來點綴 上面兩者合到一起: 裝飾器指的是定義一個函數,該函數用來為其他函數添加額外的功能 函數裝飾器分為: 無參裝飾器和有參裝飾兩種,二者的實現原理一樣,都是’函數嵌套+閉包+函數對象’的組合使用的產物。 ...


裝飾器

(1)什麼是裝飾器:

  • 器指的是工具,可以定義成函數

  • 裝飾指的是為其他事務添加額外的東西來點綴

上面兩者合到一起:

  • 裝飾器指的是定義一個函數,該函數用來為其他函數添加額外的功能

函數裝飾器分為:

  • 無參裝飾器和有參裝飾兩種,二者的實現原理一樣,都是’函數嵌套+閉包+函數對象’的組合使用的產物。

(2)為何要用裝飾器

開放封閉原則

開放:指的是對擴展功能是開放的

封閉:指的是對修改源代碼是封閉的

裝飾器就是在不修改被裝飾器對象的源代碼以及調用方式的前提下,為被裝飾對象添加新功能

(3)裝飾器實現思路

無參裝飾器

  • 方案一:失敗

沒有修改被裝飾對象的調用方式,但是改變了源代碼

def index(x, y):
    start = time.time()
    time.sleep(2)
    print(' index %s %s  ' % (x, y))
    end = time.time()
    print(end - start)

index(1, 2)
# index()
  • 方案二
# 問題:沒有修改被裝修飾對象的源代碼,也滅有修改調用方式,但是代碼冗餘
def index(x, y):
    time.sleep(2)
    print(' index %s %s  ' % (x, y))


start = time.time()
index(11, 22)
end = time.time()

start = time.time()
index(11, 22)
end = time.time()
  • 方案三

問題:解決了代碼榮譽,但是函數的調用方式改變了

def index(x, y):
    time.sleep(2)
    print(' index %s %s  ' % (x, y))


def wrapper():
    start = time.time()
    index(11, 22)
    end = time.time()


wrapper()
wrapper()
wrapper()
  • 方案三的優化一:

在方案三基礎上優化代碼:將Index寫活了(參數寫活了)

def index(x, y,z):
    time.sleep(2)
    print(' index %s %s %s ' % (x, y,z))



def wrapper(*args,**kwargs):
    start=time.time()
    index(*args,**kwargs)
    stop=time.time()
    print(stop-start)
wrapper(11,22,44)
wrapper(111,y=111,z=4545)
  • 方案三的優化二:

  • 在優化一的基礎上把被裝飾對象寫活,原來只能裝飾Index

def index(x, y, z):
    time.sleep(2)
    print(' index %s %s %s ' % (x, y, z))


def home(name):
    time.time()
    print('welcome %s to home page' % name)


def outer(func):  # func=index的記憶體地址
    # func = index
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)  # index的記憶體地址()
        stop = time.time()
        print(stop - start)

    return wrapper


home=outer(home)
home('zhao')

# f = outer(index)  # f=outer(index的記憶體地址)
# f(x=1, y=2, z=3)
index = outer(index)  # 偷梁換柱.>>此時的index指向的是wrapper的記憶體地址
print(index)
index(x=1, y=2, z=3)
  • 方案三的優化三

    將wrapper做的跟被裝飾器一摸一樣,以假亂真

def index(x, y, z):
    time.sleep(2)
    print(' index %s %s %s ' % (x, y, z))


def home(name):
    time.time()
    print('welcome %s to home page' % name)
    return 1234


def outer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res

    return wrapper


home = outer(home)
res = home('zhao')
print('返回值:>>>', res)

(4) 語法糖:

在被裝飾對象正上方的單獨一行寫 @裝飾器名字

# 裝飾器
def timmer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop - start)
        return res

    return wrapper


@timmer  # index=timmer(index)
def index(x, y, z):
    time.sleep(2)
    print(' index %s %s %s ' % (x, y, z))


@timmer  # home = timmer(home)
def home(name):
    time.sleep(2)
    print('welcome %s to home page' % name)
    return 1234



index(x=1,y=2,z=3)
home('zhao')

(5)偷梁換柱

即將原函數名指向的記憶體地址偷梁換柱,所以應該將wrapper做的跟原函數一樣才行

手動的將原函數的屬性值賦值給wrapper,需要一個一個的去加,太麻煩

def outter(func):
   
    def wrapper(*args, **kwargs):
        #手動的將原函數的屬性值賦值給wrapper
        # 函數名wrapper.__name__ =原函數.__name__
        # 函數名wrapper.__doc__ = 原函數.__doc__
        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        res = func(*args, **kwargs)
        return res

    return wrapper


@outter  # index=outter(index)
def index(x, y):
    """

    :param x:
    :param y:
    :return:
    """
    print('index:', x, y)


index(1, 2)
print(index.__name__)
print(index.__doc__)  # help(index)

自動的將原函數的所有屬性值賦值給wrapper

from functools import  wraps
def outter(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        #手動的將原函數的屬性值賦值給wrapper
        # 函數名wrapper.__name__ =原函數.__name__
        # 函數名wrapper.__doc__ = 原函數.__doc__
        # wrapper.__name__ = func.__name__
        # wrapper.__doc__ = func.__doc__
        res = func(*args, **kwargs)
        return res

    return wrapper


@outter  # index=outter(index)
def index(x, y):
    """

    :param x:
    :param y:
    :return:
    """
    print('index:', x, y)


index(1, 2)
print(index.__name__)
print(index.__doc__)  # help(index)
  • 無參裝飾器模板
def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return  res
    return wrapper()
  • 統計時間的裝飾器
def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        end=time.time()
        print(end-start)
        return  res
    return wrapper()
  • 認證功能
def auth(func):
    def wrapper(*args, **kwargs):
        name = input('your name :').strip()
        passwd = input('your password:').strip()
        if name == 'zhao' and passwd == '132':
            res = func(*args, **kwargs)
            return res
        else:
            print("your name or your password is error")

    return wrapper


@auth
def index():
    print('from index')


index()

  • 有參裝飾器模板
def 有參裝飾器(x,y,z)
    def outter(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return  res
        return wrapper()
@有參裝飾器(1,y=2,z=3)
def 被裝飾對象():
    pass
  • 認證功能改進
def auth(db_type):
    def deco(func):
        def wrapper(*args, **kwargs):
            username = input('your name:').strip()
            password = input('your paddword').strip()
            if db_type == 'file':
                print('基於文件驗證')
                if username == 'zhao' and password == '133':
                    print('login successful')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('username or password  is error')
            elif db_type == 'mysql':
                print("基於資料庫")
            elif db_type == 'ldap':
                print("基於ldap")
            else:
                print('不支持該db_type')

        return wrapper
    return deco


@auth(db_type='file')#@deco #index=dexo(index)
def index(x, y):
    print('index:>>%s %s' % (x, y))


@auth(db_type='mysql')
def home(name):
    print('home :>>%s' % name)


@auth(db_type='ldap')
def transfer():
    print("transfer:>>>%s" % transfer)


index(1, 2)
home('zhao')
transfer()
  • 疊加多個裝飾器分析
def deco1(func1):  # func1=wrapper2的記憶體地址
    def wrapper1(*args, **kwargs):
        print('deco1.wrapper1')
        res1 = func1(*args, **kwargs)
        return res1

    return wrapper1


def deco2(func2):  # func2=wrapper3的記憶體地址
    def wrapper2(*args, **kwargs):
        print('deco1.wrapper2')
        res2 = func2(*args, **kwargs)
        return res2

    return wrapper2


def deco3(x):
    def outter(func3):  # func3=被裝飾對象index函數的記憶體地址
        def wrapper3(*args, **kwargs):
            print('deco3.outter.wrapper3')
            res3 = func3(*args, **kwargs)
            return res3

        return wrapper3

    return outter


# 載入順序:自下而上
@deco1  # index=deco1(wrapper2的記憶體地址)    ===》index=wrapper1的記憶體地址
@deco2  # index=deco2(wrapper3的記憶體地址)===》index=wrapper2的記憶體地址
@deco3(11)  # @outer===>@index=outer(index)===>index=wrapper3的記憶體地址
def index(x, y):
    print('from index %s %s' % (x, y))


print(index)

# 執行順序:自上而下即 wrapper1>wrapper2>wrapper3
#
index(1, 2)  # wrapper1(1,2)

本文來自博客園,作者:Expiredsaury,轉載請註明原文鏈接:https://www.cnblogs.com/saury/p/16739191.html


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

-Advertisement-
Play Games
更多相關文章
  • 1 ES Module 規範 ES Module 是目前使用較多的模塊化規範,在 Vue、React 中大量使用,大家應該非常熟悉。TypeScript 中的模塊化與 ES 類似。 1.1 導出模塊 導出模塊有兩種方式:按需導出 和 預設導出。 按需導出是使用 export 關鍵字,將需要導出的成員 ...
  • HTML 中的 JavaScript 前言 在上一篇文章"什麼是JavaScript?"中我們說到js作為一門和頁面交互的語言。那如何把網頁的主導語言HTML和JavaScript關聯起來呢?在js早期,網景公司創造出了<script>元素,用來講JavaScript插入到HTML中。 <scrip ...
  • 一、開發優化一 1.使用Vant Weapp 1.1 什麼是Vant Weapp Vant Weapp官網鏈接 Vant Weapp是有贊前端團隊開源的一套小程式UI組件庫,助力開發者快速搭建小程式應用。它所使用的是MIT開源許可協議,對商業使用比較友好。 1.2 安裝Vant Weapp組件庫 安 ...
  • 什麼是JavaScript? 前言 本文內容為 博主閱讀“紅寶書”之後的總結和個人理解,有什麼錯誤歡迎指正! 一句話概括語言的誕生 1995年,網景公司一位名叫Brendan Eich的工程師,開發了一個叫Mocha的腳本語言。後來改名叫 JavaScript,以便蹭當時大火的Java的熱度。 到底 ...
  • 方法:定位,外邊距,內邊距,層級,邊框; 一個元素; 兩個元素; 三個元素. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=e ...
  • 輸出100個hello world. for (var i = 1; i <= 100; i++) { console.log("hello world");} 創建一個包含1~100的數組. var array = [];for (var i = 1; i <= 100; i++) { array ...
  • @(vue2.x引入threejs) vue2.x引入threejs npm安裝 npm install three 使用指定版本: npm install three@<版本號> 其他插件 因為本次開發需要引入3D模型,所以需要使用 MTLLoader, OBJLoader兩種載入器,因為開發需求 ...
  • #背景 學習前端新框架、新技術。如果需要做一些資料庫的操作來增加demo的體驗(CURD流程可以讓演示的體驗根據絲滑) 最開始的時候一個演示程式我們會調用後臺,這樣其實有一點弊端,就是增加了開發和維護成本,簡單的一個demo不應該勞師動眾 後來我會在demo中使用一些websql,奈何,websql ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...