1. 程式人生 > >python開發裝飾器的應用

python開發裝飾器的應用

urn 參數 username 什麽 alt 添加 file 回憶 h+

一、 裝飾器

  裝飾器就是閉包函數的一種應用場景

  什麽是閉包函數?我們再來回憶一下:

  閉包函數:

    定義在函數內部的函數,並且該函數包含對外部函數作用域(強調:對全局作用域名字的引用不算閉包)名字的引用,該函數稱為閉包函數

  說到作用域我們再回憶下:

  作用域:
      全局範圍:內置+全局
        全局有效,全局存活
      局部範圍:局部
        局部有效,局部存活
  作用域關系是在函數定義階段就規定死,與調用位置無關,
  也就是說,無論函數在哪兒調用,都必須回到當初定義函數時的位置找作用域關系

一 、為何要用裝飾器

#開放封閉原則:
    #軟件一旦上線後,就應該遵循開放封閉原則,對修改源代碼是封閉的,對功能的擴展是開放的,也就是我們必須找到一種解決方法:
    #能夠在不修改一個功能源代碼以及調用方式的前提下,為其加上新功能.

二 、什麽是裝飾器

#裝飾器他人的器具,本身可以是任意可調用對象,被裝飾者也可以是任意可調用對象。
#強調裝飾器的原則:
    #1 、不修改被裝飾對象的源代碼 
    #2 、不修改被裝飾對象的調用方式
#裝飾器的目標:
    #在遵循1和2的前提下,為被裝飾對象添加上新功能

三 、裝飾器的使用

技術分享圖片
#我們現在給下面函數增加一個運行時間的功能
import time

def index():
    time.sleep(3)
    print(‘welcome to index page‘)

#修改一
def index():
    start_time=time.time()
    time.sleep(3)
    print(‘welcome to index page‘)
    stop_time=time.time()
    print(‘run time is %s‘ %(stop_time-start_time))

index()   #功能實現

#評語:直接改源代碼,這麽搞被開了。。。
技術分享圖片 技術分享圖片
#再來一位童靴,來實現功能
#修改二
import time

def index():
    time.sleep(1)
    print(‘welcome to index page‘)

start_time=time.time()
index()
stop_time = time.time()
print(‘run time is %s‘ % (stop_time - start_time))

#評語:有好多函數要實現這個功能,寫N遍變這個代碼,
#後期維護一臉懵逼,還是被開。。。
技術分享圖片 技術分享圖片
#再來一位童靴,功能重復實現看我用強大的函數
#修訂三:
import time

def index():
    time.sleep(3)
    print(‘welcome to index page‘)

def wrapper(func):  #func=index
    start_time=time.time()
    func() #index()
    stop_time = time.time()
    print(‘run time is %s‘ % (stop_time - start_time))

wrapper(index) 

#評語:修改了原函數的調用方式,依然被開。。。
技術分享圖片 技術分享圖片
#終於來了位小牛的童靴,函數的值可以返回,然後我再把重新定義index
#修訂四:
import time

def index():
    time.sleep(3)
    print(‘welcome to index page‘)

def outter(func): #func=最原始的index
    # func=最原始的index
    def wrapper():
        start_time=time.time()
        func()
        stop_time=time.time()
        print(stop_time-start_time)
    return wrapper

index=outter(index)  # 新的index=wrapper
index()  #wrapper()  功能基本實現
#評語:在原值沒有返回值是沒問題,但是有返回值的情況下,這麽搞就會發現返回的是None
技術分享圖片 技術分享圖片
#這次小牛牛童靴路過看到這情況,say這麽搞!
#修訂五
import time
def index():
    time.sleep(1)
    print(‘welcome to index page‘)
    return 123     #假使這裏返回123     返回值可以是任意類型 

#==============下面就是裝飾器
def timmer(func):
    #func=最原始的index
    def wrapper(*args,**kwargs):     #可變長參數
        start_time=time.time()
        res=func(*args,**kwargs)  #調用最原始的index
        stop_time=time.time()
        print(stop_time-start_time)
        return res        #index()運行的返回值
    return wrapper

index=timmer(index) # 新的index=wrapper

print(index())    #功能已經實現,返回值123

#評語:這裏的裝飾的功能已經實現,返回的值也得到,小牛牛不是白叫的
技術分享圖片 技術分享圖片
#天上五彩紅光,大牛童靴出現!各位童靴火速圍觀!
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print(stop_time-start_time)
        return res
    return wrapper

@timmer #index=timmer(index)      裝飾器的標準格式!
def index():
    time.sleep(1)
    print(‘welcome to index page‘)
    return 123

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

# index()
#home(‘egon‘)   

#評語:大牛就是大牛!大牛say:教你們絕技,不會的童靴可以照下面的模板實現:

#無參裝飾器模板
def outer(func):     #outer,inner名字功能隨意
    def inner(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return inner

@outer            #裝飾器要在裝飾函數的上方
def duoduo():
    pass                      
技術分享圖片

四 、裝飾器語法

技術分享圖片
#被裝飾函數的正上方,單獨一行
@deco1
@deco2
@deco3
def foo():
    pass
#foo=deco1(deco2(deco3(foo)))
#這裏的思想就是最上面裝飾器,裝飾的下面所有的函數(deco2,deco3,foo)
#然後deco2裝飾(deco3,foo),最後deco3裝飾foo
#功能的不同,放的順序也要註意,不然裝飾的效果可能實現的就不對了!
技術分享圖片

五、多個裝飾器的使用:

技術分享圖片
import time
current_user={
    ‘username‘:None,
    # ‘login_time‘:None
}

def auth(func):
    # func=index
    def wrapper(*args,**kwargs):
        if current_user[‘username‘]:   #這裏是認證過的,下次就不用認證
            print(‘已經登陸過了‘)
            res=func(*args,**kwargs)
            return res

        uname=input(‘用戶名>>: ‘).strip()
        pwd=input(‘密碼>>: ‘).strip()
        if uname == ‘egon‘ and pwd == ‘123‘:
            print(‘登陸成功‘)
            current_user[‘username‘]=uname
            res=func(*args,**kwargs)
            return res
        else:
            print(‘用戶名或密碼錯誤‘)
    return wrapper

def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print(stop_time-start_time)
        return res
    return wrapper

@timmer # timmer 統計的是auth+index的執行時間
@auth             #我們只裝飾index的話就要把@timmer緊跟index
def index():
    time.sleep(1)
    print(‘welcome to index page‘)
    return 123

@auth
@timmer     #這裏統計的就是home運行的時間
def home(name):
    time.sleep(2)
    print(‘welcome %s to home page‘ %name)

#index()
#home("duoduo")
技術分享圖片

六、有參數的裝飾器的使用:

技術分享圖片
import time
current_user={
    ‘username‘:None,
    # ‘login_time‘:None
}

def auth(engine):    #道理還是那個道理,在外面包了一層engine的值
    # engine=‘file‘  #這個值外面穿什麽進來就是什麽
    def auth2(func):
        # func=index
        def wrapper(*args,**kwargs):
            if engine == ‘file‘:
                if current_user[‘username‘]:
                    print(‘已經登陸過了‘)
                    res=func(*args,**kwargs)
                    return res

                uname=input(‘用戶名>>: ‘).strip()
                pwd=input(‘密碼>>: ‘).strip()
                if uname == ‘egon‘ and pwd == ‘123‘:
                    print(‘登陸成功‘)
                    current_user[‘username‘]=uname
                    res=func(*args,**kwargs)
                    return res
                else:
                    print(‘用戶名或密碼錯誤‘)
            elif engine == ‘mysql‘:         #engine 值得判斷情況
                print(‘基於MyQL的認證‘)
            elif engine == ‘ldap‘:
                print(‘基於LDAP的認證‘)
        return wrapper
    return auth2     #這裏也要返回auth2的內存地址

@auth(‘ldap‘) #@auth2 #index=auth2(index) #index=wrapper
def index():
    time.sleep(1)
    print(‘welcome to index page‘)
    return 123


index() # wrapper()
技術分享圖片

python開發裝飾器的應用