溫馨提示×

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

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

Python學(xué)習(xí)—裝飾器

發(fā)布時(shí)間:2020-07-21 16:13:43 來(lái)源:網(wǎng)絡(luò) 閱讀:449 作者:長(zhǎng)安223 欄目:編程語(yǔ)言

裝飾器

裝飾器實(shí)際上就是為了給某程序增添功能,但該程序已經(jīng)上線或已經(jīng)被使用,那么就不能大批量的修改源代碼,這樣是不科學(xué)的也是不現(xiàn)實(shí)的,因?yàn)榫彤a(chǎn)生了裝飾器,使得其滿足:
(1).不能修改被裝飾的函數(shù)的源代碼
(2).不能修改被裝飾的函數(shù)的調(diào)用方式
(3).滿足(1)、(2)的情況下給程序增添功能
實(shí)現(xiàn):
我們寫一個(gè)嵌套函數(shù),在內(nèi)部函數(shù)中添加新功能新內(nèi)容,然后調(diào)用原函數(shù),再在外部函數(shù)return這個(gè)內(nèi)部函數(shù)。由于函數(shù)也是一個(gè)對(duì)象,而且函數(shù)對(duì)象可以被賦值給變量,所以我們調(diào)用這個(gè)嵌套函數(shù),把返回值(返回的是一個(gè)函數(shù))賦給一個(gè)和原函數(shù)同名的變量,通過(guò)變量也能調(diào)用該函數(shù)。這樣就實(shí)現(xiàn)了不改變?cè)瘮?shù)代碼增強(qiáng)其功能。

舉個(gè)例子:
已經(jīng)有一個(gè)函數(shù)login()實(shí)現(xiàn)的登陸功能,我想給它添加一些新內(nèi)容:

def desc(fun):
    def add_info():
        print('寫在前面.......')
        fun()
        print('寫在后面........')
    return add_info

def login():
    print('login............')

login = desc(login)
login()

運(yùn)行:
Python學(xué)習(xí)—裝飾器

其實(shí)這就是一個(gè)裝飾器的雛形了。但是在使用這個(gè)裝飾器裝飾的時(shí)候,需要在每個(gè)函數(shù)前面加上這樣一句代碼:

    function = desc(function)

顯然有些麻煩,Python提供了一種語(yǔ)法糖來(lái)代替它:

@裝飾器名    #注意要把這句代碼放到原函數(shù)前

代碼修改如下,會(huì)時(shí)同樣的效果且更加簡(jiǎn)潔:

def desc(fun):
    def add_info():
        print('寫在前面.......')
        fun()
        print('寫在后面........')
    return add_info

@desc
def login():
    print('login............')

login()

Python學(xué)習(xí)—裝飾器

用裝飾器來(lái)實(shí)現(xiàn)計(jì)時(shí)功能:
計(jì)算在字符串連接時(shí),+和join那個(gè)效率更高。

import random,string,time
li = [random.choice(string.ascii_letters) for i in range(1000)]    #這里用了1000個(gè)字符來(lái)比較

def desc(fun):
    def wrapper():
        start_time = time.time()
        fun()
        end_time = time.time()
        print('%.8f' %(end_time-start_time))
    return wrapper

@desc
def a_add():
    st = ''
    for i in li:
        st += (i+',')
    return st
@desc
def b_add():
    return ','.join(li)

a_add()
b_add()

運(yùn)行:
Python學(xué)習(xí)—裝飾器

顯然,此時(shí)join方式效率要高于連接符+
可以看到以上內(nèi)容中函數(shù)沒有參數(shù),即是無(wú)參函數(shù)裝飾器。

裝飾有參函數(shù)

用裝飾器來(lái)實(shí)現(xiàn)判斷變量類型:
對(duì)函數(shù)的傳入?yún)?shù)進(jìn)行檢查,參數(shù)符合要求(整數(shù))正常調(diào)用函數(shù),不符合要求則報(bào)錯(cuò)。

import functools   #導(dǎo)入functools包

def required_int(fun):
    @functools.wraps(fun)   #functools包中的wraps函數(shù)保證原函數(shù)的信息不因裝飾器而改變。
    def wrapper(*args):    #可變參數(shù)
        for i in args:
            if not str(i).isdigit():    #或者用isinstance(i,int)更加簡(jiǎn)潔
                print('參數(shù)必須是整數(shù)...')
                return None
                break
        else:
            res=fun(*args)
            return res
    return wrapper

@required_int
def add_num(*args):   #參數(shù)為可變參數(shù)
    sum = 0
    for i in args:
        sum += i
    return sum

print('參數(shù)合法輸出結(jié)果:')
print(add_num(2,3,45,6))
print('參數(shù)不合法輸出結(jié)果:')
print(add_num(3,4,5,'e'))

運(yùn)行:
Python學(xué)習(xí)—裝飾器

模仿博客邏輯功能:
登陸后才能寫博客,瀏覽博客不需要登陸。

import functools

login_users = ['admin','root']     #已經(jīng)登陸的用戶
def is_login(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):    #可變參數(shù)加關(guān)鍵字參數(shù)
        if kwargs.get('name') in login_users:
            res = fun(*args,**kwargs)
        else:
            res = login()
        return res
    return wrapper

def login():
    return '請(qǐng)先登陸.......'

@is_login
def writtelog(name):
    return '寫博客日之.....'

def viewnews():
     return '看文章新聞.....'

print(writtelog(name='root'))
print(writtelog(name='root11'))
print(viewnews())

運(yùn)行:
Python學(xué)習(xí)—裝飾器

多個(gè)裝飾器的使用

模仿只有admin用戶在登陸后才能進(jìn)入后臺(tái):

import functools

login_users = ['admin','root']     #已經(jīng)登陸的用戶

def is_admin(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        if kwargs.get('name')=='admin':
            res = fun(*args,**kwargs)
        else:
            res = '沒有權(quán)限....'
        return res
    return wrapper

def is_login(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        if kwargs.get('name') in login_users:
            res = fun(*args,**kwargs)
        else:
            res = login()
        return res
    return wrapper

def login():
    return '請(qǐng)先登陸.......'

@is_login
@is_admin
def houtai(name):
    return '進(jìn)入后臺(tái).....'

print(houtai(name='admin'))
print(houtai(name='root'))
print(houtai(name='uuuu'))

運(yùn)行:
Python學(xué)習(xí)—裝飾器

這里需要注意的是,能用一個(gè)裝飾器盡量不要使用多個(gè)裝飾器。
當(dāng)有多個(gè)裝飾器時(shí),從上往下依次調(diào)用裝飾器,真實(shí)的wrapper內(nèi)容時(shí)從上到下執(zhí)行的。

帶參數(shù)的裝飾器

一個(gè)裝飾器,對(duì)不同的函數(shù)有不同的裝飾。那么就需要知道對(duì)哪個(gè)函數(shù)采取哪種裝飾。因此,就需要裝飾器帶一個(gè)參數(shù)來(lái)標(biāo)記一下。
將參數(shù)傳入,我們需要再加一層函數(shù)嵌套,來(lái)傳遞裝飾器的參數(shù)。
舉栗子:
對(duì)兩個(gè)不同的函數(shù)計(jì)時(shí):

import time
def timer(parameter):    #參數(shù)parameter傳入調(diào)用的函數(shù)的名字
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            if parameter == 'task1':
                start_time = time.time()
                func(*args, **kwargs)
                stop_time = time.time()
                print("the task1 run time is :", stop_time - start_time)
            elif parameter == 'task2':
                start_time = time.time()
                func(*args, **kwargs)
                stop_time = time.time()
                print("the task2 run time is :", stop_time - start_time)
        return wrapper
    return outer_wrapper

@timer(parameter='task1')
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2')
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

運(yùn)行:
Python學(xué)習(xí)—裝飾器

判斷函數(shù)傳入?yún)?shù)升級(jí)版本:

import functools,math

def required(*argss):
    def required_01(fun):
        @functools.wraps(fun)
        def wrapper(*args):
            for i in args:
                if not isinstance(i,argss):    #或者用isinstance(i,int)
                    print('參數(shù)必須是:',*argss)
                    return None
                    break
            else:
                res=fun(*args)
                return res
        return wrapper
    return required_01

@required(str,int)  #這里可以看到要求傳入?yún)?shù)時(shí)str或者int
def add_num(*args):
    return args

print(add_num('ssss',789))    #符合要求
print(add_num('aaaa',693,3.14))     #有浮點(diǎn)型,不符合要求

運(yùn)行:
Python學(xué)習(xí)—裝飾器

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

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

AI