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 [email protected]<版本號> 其他插件 因為本次開發需要引入3D模型,所以需要使用 MTLLoader, OBJLoader兩種載入器,因為開發需求 ...
  • #背景 學習前端新框架、新技術。如果需要做一些資料庫的操作來增加demo的體驗(CURD流程可以讓演示的體驗根據絲滑) 最開始的時候一個演示程式我們會調用後臺,這樣其實有一點弊端,就是增加了開發和維護成本,簡單的一個demo不應該勞師動眾 後來我會在demo中使用一些websql,奈何,websql ...
一周排行
    -Advertisement-
    Play Games
  • 前言 當別人做大數據用Java、Python的時候,我使用.NET做大數據、數據挖掘,這確實是值得一說的事。 寫的並不全面,但都是實際工作中的內容。 .NET在大數據項目中,可以做什麼? 寫腳本(使用控制台程式+頂級語句) 寫工具(使用Winform) 寫介面、寫服務 使用C#寫代碼的優點是什麼? ...
  • 前言 本文寫給想學C#的朋友,目的是以儘快的速度入門 C#好學嗎? 對於這個問題,我以前的回答是:好學!但仔細想想,不是這麼回事,對於新手來說,C#沒有那麼好學。 反而學Java還要容易一些,學Java Web就行了,就是SpringBoot那一套。 但是C#方向比較多,你是學控制台程式、WebAP ...
  • 某一日晚上上線,測試同學在回歸項目黃金流程時,有一個工單項目介面報JSF序列化錯誤,馬上升級對應的client包版本,編譯部署後錯誤消失。 線上問題是解決了,但是作為程式員要瞭解問題發生的原因和本質。但這都是為什麼呢? ...
  • 本文介紹基於Python語言中TensorFlow的Keras介面,實現深度神經網路回歸的方法。 1 寫在前面 前期一篇文章Python TensorFlow深度學習回歸代碼:DNNRegressor詳細介紹了基於TensorFlow tf.estimator介面的深度學習網路;而在TensorFl ...
  • 前段時間因業務需要完成了一個工作流組件的編碼工作。藉著這個機會跟大家分享一下整個創作過程,希望大家喜歡,組件暫且命名為"easyFlowable"。 接下來的文章我將從什麼是工作流、為什麼要自研這個工作流組件、架構設計三個維度跟大家來做個整體介紹。 ...
  • 1 簡介 我們之前使用了dapr的本地托管模式,但在生產中我們一般使用Kubernetes托管,本文介紹如何在GKE(GCP Kubernetes)安裝dapr。 相關文章: dapr本地托管的服務調用體驗與Java SDK的Spring Boot整合 dapr入門與本地托管模式嘗試 2 安裝GKE ...
  • 摘要:在jvm中有很多的參數可以進行設置,這樣可以讓jvm在各種環境中都能夠高效的運行。絕大部分的參數保持預設即可。 本文分享自華為雲社區《為什麼需要對jvm進行優化,jvm運行參數之標準參數》,作者:共飲一杯無。 我們為什麼要對jvm做優化? 在本地開發環境中我們很少會遇到需要對jvm進行優化的需 ...
  • 背景 我們的業務共使用11台(阿裡雲)伺服器,使用SpringcloudAlibaba構建微服務集群,共計60個微服務,全部註冊在同一個Nacos集群 流量轉發路徑: nginx->spring-gateway->業務微服務 使用的版本如下: spring-boot.version:2.2.5.RE ...
  • 基於php+webuploader的大文件分片上傳,帶進度條,支持斷點續傳(刷新、關閉頁面、重新上傳、網路中斷等情況)。文件上傳前先檢測該文件是否已上傳,如果已上傳提示“文件已存在”,如果未上傳則直接上傳。視頻上傳時會根據設定的參數(分片大小、分片數量)進行上傳,上傳過程中會在目標文件夾中生成一個臨 ...
  • 基於php大文件分片上傳至七牛雲,使用的是七牛雲js-sdk V2版本,引入js文件,配置簡單,可以暫停,暫停後支持斷點續傳(刷新、關閉頁面、重新上傳、網路中斷等情況),可以配置分片大小和分片數量,官方文檔https://developer.qiniu.com/kodo/6889/javascrip ...