溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

python裝飾器相關(guān)知識(shí)點(diǎn)

發(fā)布時(shí)間:2022-03-02 12:26:07 來源:億速云 閱讀:148 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下python裝飾器相關(guān)知識(shí)點(diǎn),相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

一、裝飾器

1.相關(guān)知識(shí)點(diǎn)

  • *args:負(fù)責(zé)將多余的位置實(shí)參匯總,賦值給args

  • **kwargs:負(fù)責(zé)將多余的關(guān)鍵字實(shí)參匯總,賦值給kwargs

命名空間與作用域

函數(shù)對(duì)象:

  • 可以把函數(shù)當(dāng)成參數(shù)傳入

  • 可以把函數(shù)當(dāng)做返回值返回

函數(shù)的嵌套定義:在函數(shù)內(nèi)定義函數(shù)

閉包函數(shù):父函數(shù)的返回值為一個(gè)函數(shù),被返回的函數(shù)調(diào)用了父函數(shù)的局部變量,且該函數(shù)可以在父函數(shù)外部執(zhí)行

裝飾器:

裝飾器:定義一個(gè)為其他函數(shù)添加功能的函數(shù)

為什么要使用裝飾器?

  • 開放封閉原則:開放擴(kuò)展功能但封閉源代碼的修改

  • 裝飾器就是在不修改裝飾對(duì)象源代碼以及調(diào)用方式的前提下,為裝飾對(duì)象添加新功能

裝飾器實(shí)現(xiàn):

需求:不修改源代碼,計(jì)算代碼執(zhí)行時(shí)間

 源代碼:

import time
def func0(x):
    time.sleep(1)
    print(x)
func0(0)

# 方案一:實(shí)現(xiàn)了計(jì)算代碼執(zhí)行時(shí)間的功能,但修改了源代碼,不符合要求
def func1(x):
    start = time.time()
    time.sleep(1)
    print(x)
    end = time.time()
    print('方案一 運(yùn)行時(shí)間:{}'.format(end - start))
func1(1)

# 方案二:滿足要求,但代碼不具備重用性
start = time.time()
func0(2)
end = time.time()
print('方案二 運(yùn)行時(shí)間:{}'.format(end - start))

# 方案三:將方案二封裝為函數(shù),但修改了函數(shù)的調(diào)用方式,不符合要求
def wrapper():
    start = time.time()
    func0(3)
    end = time.time()
    print('方案三 運(yùn)行時(shí)間:{}'.format(end - start))
wrapper()

# 方案四:不修改原函數(shù)的調(diào)用方式
def wrapper(x):
    start = time.time()
    func0(x)
    end = time.time()
    print('方案四 運(yùn)行時(shí)間:{}'.format(end - start))
wrapper(4)

# 方案五:方案四參數(shù)被寫死,優(yōu)化方案四
def wrapper(*args, **kwargs):
    start = time.time()
    func0(*args, **kwargs)
    end = time.time()
    print('方案五 運(yùn)行時(shí)間:{}'.format(end - start))
wrapper(5)

# 方案六:方案五函數(shù)被寫死,優(yōu)化方案五,但修改了源代碼的調(diào)用方式
def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('方案六 運(yùn)行時(shí)間:{}'.format(end - start))
    return wrapper
f = outter(func0)
f(6)

# 方案七:優(yōu)化方案六(outter即為裝飾器,用于裝飾func0)
def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('方案七 運(yùn)行時(shí)間:{}'.format(end - start))
    return wrapper
func0 = outter(func0)    # 偷梁換柱,調(diào)用者無感知
func0(7)

運(yùn)行結(jié)果:

0
1
方案一 運(yùn)行時(shí)間:1.001857042312622
2
方案二 運(yùn)行時(shí)間:1.0040733814239502
3
方案三 運(yùn)行時(shí)間:1.0017154216766357
4
方案四 運(yùn)行時(shí)間:1.007995367050171
5
方案五 運(yùn)行時(shí)間:1.0145602226257324
6
方案六 運(yùn)行時(shí)間:1.0046615600585938
7
方案七 運(yùn)行時(shí)間:1.0094060897827148

2.語法糖

  • 使用語法糖,需要將裝飾器放到被裝飾對(duì)象前

# 不使用語法糖
import time
def func0(x):
    time.sleep(1)
    print(x)
# func0(0)

def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('運(yùn)行時(shí)間:{}'.format(end - start))
    return wrapper
func0 = outter(func0)
func0('hello')

# 使用語法糖
import time

def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('運(yùn)行時(shí)間:{}'.format(end - start))
    return wrapper

@outter # 語法糖,等價(jià)于該行func0 = outter(func0)
def func0(x):
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)

運(yùn)行結(jié)果:

hello
運(yùn)行時(shí)間:1.0050427913665771
wrapper

3.裝飾器模板

 # 裝飾器模板
 def decorator_name(x):
     def wrapper(*args, **kwargs):
         # ...新添加的功能...
         # 下為調(diào)用原函數(shù)的格式
         x(*args, **kwargs)
     return wrapper

@decorator_name
 def func_name():
     pass

擴(kuò)展:真正實(shí)現(xiàn)偷梁換柱,調(diào)用者無感知

  • 不使用裝飾器

 # 不使用裝飾器
import time


def func0(x):
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__) # 查看函數(shù)名 
print(help(func0)) # 查看幫助信息(主要為注釋內(nèi)容)

運(yùn)行結(jié)果:

hello
func0
Help on function func0 in module main:

func0(x)
這是函數(shù)

None

使用裝飾器:

import time

def outter(a):
    def wrapper(*args, **kwargs):
        '''這是裝飾器'''
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('運(yùn)行時(shí)間:{}'.format(end - start))
    return wrapper

@outter # func0 = outter(func0)
def func0(x):
    '''這是函數(shù)'''
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)
print(help(func0))

運(yùn)行結(jié)果:

hello
運(yùn)行時(shí)間:1.011878490447998
wrapper
Help on function wrapper in module main:

wrapper(*args, **kwargs)
這是裝飾器

None

嘔吼,露餡了

  • 解決方法,將原函數(shù)的屬性和方法,賦值給裝飾器內(nèi)的wrapper

import time

def outter(a):
    def wrapper(*args, **kwargs):
        '''這是裝飾器'''
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('運(yùn)行時(shí)間:{}'.format(end - start))
    wrapper.__name__ = a.__name__
    wrapper.__doc__ = a.__doc__
    return wrapper

@outter # func0 = outter(func0)
def func0(x):
    '''這是函數(shù)'''
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)
print(help(func0))

運(yùn)行結(jié)果:

hello
運(yùn)行時(shí)間:1.0030155181884766
func0
Help on function func0 in module main:

func0(*args, **kwargs)
這是函數(shù)

None

但是,函數(shù)有很多屬性和方法,一個(gè)一個(gè)手動(dòng)修改過于麻煩,甚至可能會(huì)遺漏,但python也提供了解決方法

import time
from functools import wraps

def outter(a):
    @wraps(a)
    def wrapper(*args, **kwargs):
        '''這是裝飾器'''
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('運(yùn)行時(shí)間:{}'.format(end - start))
    # wrapper.__name__ = a.__name__
    # wrapper.__doc__ = a.__doc__
    return wrapper

@outter # func0 = outter(func0)
def func0(x):
    '''這是函數(shù)'''
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)
print(help(func0))

運(yùn)行結(jié)果:

hello
運(yùn)行時(shí)間:1.0114128589630127
func0
Help on function func0 in module main:

func0(x)
這是函數(shù)

None

4.有參裝飾器

  • 裝飾器內(nèi)需要傳入?yún)?shù),但是由于語法糖的限制,裝飾器只能有一個(gè)參數(shù),并且該參數(shù)僅用來接收被裝飾對(duì)象的內(nèi)存地址,如何傳入其他參數(shù)?

  • 解決思路:將裝飾器做成閉包函數(shù)

def outter(db_type):
    # 裝飾器auth
    def auth(x):
        def wrapper(*args, **kwargs):
            if db_type == 'file':
                name = input('請(qǐng)輸入姓名:')
                passwd = input('請(qǐng)輸入密碼:')
                if name == 'zhangsan' and passwd == '123':
                    x(*args, **kwargs)
                    print('基于文件認(rèn)證')
                else:
                    print('用戶名或密碼錯(cuò)誤,認(rèn)證失敗')
            elif db_type == 'mysql':
                x(*args, **kwargs)
                print('基于數(shù)據(jù)庫認(rèn)證')
            else:
                print('未知認(rèn)證方式,不支持')
        return wrapper
    return auth

# 函數(shù)
auth = outter(db_type='file')
@auth
def file():
    print('hello')


auth = outter(db_type='mysql')
@auth
def mysql():
    print('world')

msg = input('請(qǐng)選擇認(rèn)證方式(1=file|2=mysql):').strip()
if msg =='1':
    file()
elif msg == '2':
    mysql()
else:
    print('不支持')
  • 將傳入的參數(shù)寫入語法糖

def outter(db_type):
    # 裝飾器auth
    def auth(x):
        def wrapper(*args, **kwargs):
            if db_type == 'file':
                name = input('請(qǐng)輸入姓名:')
                passwd = input('請(qǐng)輸入密碼:')
                if name == 'zhangsan' and passwd == '123':
                    x(*args, **kwargs)
                    print('基于文件認(rèn)證')
                else:
                    print('用戶名或密碼錯(cuò)誤,認(rèn)證失敗')
            elif db_type == 'mysql':
                x(*args, **kwargs)
                print('基于數(shù)據(jù)庫認(rèn)證')
            else:
                print('未知認(rèn)證方式,不支持')
        return wrapper
    return auth

# 函數(shù)
# auth = outter(db_type='file')
# @auth
@outter(db_type='file')
def file():
    print('hello')

# auth = outter(db_type='mysql')
# @auth
@outter(db_type='mysql')
def mysql():
    print('world')

msg = input('請(qǐng)選擇認(rèn)證方式(1=file|2=mysql):').strip()
if msg =='1':
    file()
elif msg == '2':
    mysql()
else:
    print('不支持')
  • 模板

# 有參裝飾器模板
def out_decorator_name(x,y,z):
    def decorator_name(a):
        def wrapper(*args, **kwargs):
            # ...新添加的功能...
            # 下為調(diào)用原函數(shù)的格式
            a(*args, **kwargs)
        return wrapper
    return decorator_name

@out_decorator_name(x,y,z=1)
def func_name():
     pass

以上是“python裝飾器相關(guān)知識(shí)點(diǎn)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI