【筆記】Python基礎五:裝飾器

一,什麼是裝飾器python

本質就是函數,功能是爲其餘函數添加附加功能session

原則:閉包

1,不修改被修飾函數的源代碼app

2,不修改被修飾函數的調用方式框架

例子:函數

import time

def timmer(func):
    def wapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('函數的運行時間 %s' % (stop_time - start_time))
        return res
    return  wapper

@timmer      #加裝飾器
def cal(l):  #原函數
    res = 0
    time.sleep(1)
    for i in l:
        res += i
    return res

t1 = cal(range(100))
print(t1)
'''輸出:
函數的運行時間 1.0000572204589844
4950
'''

 

二,裝飾器的知識儲備spa

裝飾器 =  高階函數 + 函數嵌套(閉包)code

(一)高階函數blog

定義:ip

1,函數接受的參數是一個函數名

2,函數的返回值是一個函數名

3,知足上述條件任意一個,均可稱之爲高階函數

例子:高階函數

def foo():
    print('你好啊林師傅')

def test(func):
    print(func) #打印內存地址
    func()      #執行函數foo

test(foo) #test是高階
'''輸出:
<function foo at 0x00000000022F22F0>
你好啊林師傅
'''

例子:函數接受的參數是一個函數名,就能實現給函數增長功能的目標

def foo():
    time.sleep(3)
    print('你好啊林師傅')

def test(func):
    start_time = time.time()
    func()      #執行函數foo
    stop_time = time.time()
    print('函數的運行時間 %s' % (stop_time - start_time))

test(foo) #修改了函數原來的調用方式,原來是foo()
'''輸出:
你好啊林師傅
函數的運行時間 3.000171661376953
'''

例子:若是再加上返回值是一個函數,則能實現「不修改被修飾函數的調用方式」這一個條件

def foo():
    print('from the foo')
def test(func):
    return func

# res = test(foo)
# print(res) #res獲得的是foo的內存地址
# res()      #實際是運行foo
foo = test(foo) #若是把函數的執行結果從新賦值給foo函數名
foo()           #就能實現「不修改被修飾函數的調用方式」這一個條件
'''輸出:
from the foo
'''

例子:多運行一次,並不合格(高階函數並不能所有知足裝飾器的須要)                1,不修改foo源代碼,2,不修改foo調用方式

def foo():
    time.sleep(3)
    print('from the foo')

def timer(func):
    start_time = time.time()
    func()      #執行函數foo
    stop_time = time.time()
    print('函數的運行時間 %s' % (stop_time - start_time))
    return func

foo = timer(foo) 
foo()           #又執行了一次函數foo  
'''輸出:
from the foo
函數的運行時間 3.007171869277954
from the foo
'''

 (二),函數嵌套

定義:在函數裏面定義另外一個函數

def bar():
    print('from bar')

def foo():
    print('from foo')
    def test():
        pass

函數閉包:

閉——封裝變量,包——層層嵌套。其實和函數做用域是一回事。特性:若是內層沒有定義重名變量,外層的變量能夠傳進來。

def father(auth_type):
    # print('from father %s' %name)
    def son():
        # name='linhaifeng_1'
        # print('個人爸爸是%s' %name)
        def grandson():
            print('個人爺爺是%s' %auth_type)#實參'filedb'從最外層一直傳導進來
        grandson()
    # print(locals())
    son()
# father('linhaifeng')
father('filedb')

#輸出:個人爺爺是filedb

 

三,裝飾器框架

使用高階函數+函數閉包完成裝飾器框架。

我的理解:1,裝飾器函數傳入的原函數名func,用在最內層運行原函數,以實現和原函數同樣的效果

                  2,而在原函數外層包裹的一層函數wrapper,加retunrn wapper返回的是加入新功能後的一個函數地址,在外面運行這個函數地址也就執行了加入的新功能

                  3,而@這樣一個語法把新函數的地址賦值給原函數名,這樣運行原函數名,就把新的功能增長了。         

def timmer(func): #func=test
    def wrapper():
        # print(func)
        start_time=time.time()
        func() #就是在運行test()
        stop_time = time.time()
        print('運行時間是%s' %(stop_time-start_time))
    return wrapper

@timmer #test=timmer(test)
def test():
    time.sleep(3)
    print('test函數運行完畢')
test()
'''輸出:
test函數運行完畢
運行時間是3.000171661376953
'''

#先使用res來接收
# res=timmer(test)  #返回的是wrapper的地址
# res()  #執行的是wrapper()

#而後使用test原函數名替換res
# test=timmer(test)  #返回的是wrapper的地址
# test()  #執行的是wrapper()

#最後python提供@語法等效於test=timmer(test)
#  @timmer  就至關於 test=timmer(test)

 例子:原函數加入返回值狀況

import time
def timmer(func):
    def wrapper():
        start_time=time.time()
        res=func()           #使用res接收返回值
        stop_time = time.time()
        print('運行時間是%s' %(stop_time-start_time))
        return res           #把返回值傳回去
    return wrapper

@timmer
def test():
    time.sleep(3)
    print('test函數運行完畢')
    return '這是test的返回值'  #這裏原函數test加入了返回值

res=test()
print(res)
'''輸出:
test函數運行完畢
運行時間是3.007171869277954
這是test的返回值
'''

例子:加入參數,而且考慮原函數參數不固定的狀況

#傳參的複習和回顧
def test2(name,age,gender): #test2(*('alex',18,'male','x','y'),**{})
    #name,age,gender=('alex',18,'male','x','y')
    print(name)
    print(age)
    print(gender)

def test1(*args,**kwargs):
    test2(*args,**kwargs)  #args=('alex',18,'male','x','y') kwargs={}

test1('alex',18,gender='male') #正確的傳參方式
test1('alex',18,'male')        #正確的傳參方式
test1('alex',18,'male','x','y')#多傳了兩個參數,報錯:test2() takes 3 positional arguments but 5 were given
#(*args,**kwargs)實現了傳遞可變參數的功能,在原函數有多個變體的狀況下,裝飾器函數能夠通用
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs) #能夠理解爲wrapper獲得的參數原封不動的傳進原函數
        print('args 是 %s:' %(args,))
        print('kwargs 是: %s' %kwargs)
        stop_time = time.time()
        print('運行時間是%s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def test(name,age):         #兩個參數
    time.sleep(1)
    print('test函數運行完畢,名字是【%s】 年齡是【%s】' %(name,age))
    return '這是test的返回值'

@timmer
def test1(name,age,gender): #三個參數
    time.sleep(1)
    print('test1函數運行完畢,名字是【%s】 年齡是【%s】 性別【%s】' %(name,age,gender))
    return '這是test1的返回值'

test('linhaifeng',age=18) #args=('linhaifeng')     kwargs={'age':18}
print('######')
test1('alex',18,'male')   #args=('alex',18,'male') kwargs={}

'''輸出: test函數運行完畢,名字是【linhaifeng】 年齡是【18】 args 是 ('linhaifeng',): kwargs 是: {'age': 18} 運行時間是1.0000572204589844 ###### test1函數運行完畢,名字是【alex】 年齡是【18】 性別【male】 args 是 ('alex', 18, 'male'): kwargs 是: {} 運行時間是1.0000572204589844 '''

兩個小技巧

#1,解壓序列
a,*_,c =[10,9,8,7,6]#表明全部值
print(a)
print(_) #下劃線_是變量
print(c)
'''輸出:
10
[9, 8, 7]
6
'''

#2,交換兩個變量值 f1 = 1 f2 = 10 f1,f2 = f2,f1 print(f1,f2) #輸出:10 1

 

四,裝飾器實戰

(一)使用裝飾器實現驗證和session功能

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}


def auth_func(func):
    def wrapper(*args,**kwargs):
        if current_dic['username'] and current_dic['login']:
            res = func(*args, **kwargs)
            return res
        username=input('用戶名:').strip()
        passwd=input('密碼:').strip()
        for user_dic in user_list:
            if username == user_dic['name'] and passwd == user_dic['passwd']:
                current_dic['username']=username
                current_dic['login']=True
                res = func(*args, **kwargs)
                return res
        else:    #######這個else很是有價值,不是if的else,而是for的else
            print('用戶名或者密碼錯誤')

    return wrapper

@auth_func
def index():
    print('歡迎來到京東主頁')

@auth_func
def home(name):
    print('歡迎回家%s' %name)

@auth_func
def shopping_car(name):
    print('%s的購物車裏有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

print('before-->',current_dic)
index()
print('after--->',current_dic)
home('產品經理')
# shopping_car('產品經理')

 

五,裝飾器運行流程

1,運行@裝飾器函數名,實際動做是運行裝飾器函數,因爲閉包函數wrapper定義在這個裝飾器函數的裏面,因此僅return wrapper,把wrapper的函數地址返回給原函數名(從新賦值)

2,當程序運行到原函數名(),也就是執行原函數時,由於前面的步驟,實際跳轉到wrapper函數執行。這裏面巧妙的地方是,wrapper函數裏面又包含一個原函數(),這回是真正執行原函數()。

3,執行完原函數後,回到wrapper裏面,原函數()下面的步驟運行。

 

六,帶參數的裝飾器函數

本質就是在外面再添加一層,把原裝飾器函數包裹起來。原裝飾器函數實現的是把原函數名傳遞進去在內層,帶參數的裝飾器函數實現傳遞功能性參數做用在最外層,層層嵌套。

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}

def auth(auth_type='filedb'):
    def auth_func(func):
        def wrapper(*args,**kwargs):
            print('認證類型是',auth_type)
            if auth_type == 'filedb':
                if current_dic['username'] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username=input('用戶名:').strip()
                passwd=input('密碼:').strip()
                for user_dic in user_list:
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_dic['username']=username
                        current_dic['login']=True
                        res = func(*args, **kwargs)
                        return res
                else:
                    print('用戶名或者密碼錯誤')
            elif auth_type == 'ldap':
                print('鬼才特麼會玩')
                res = func(*args, **kwargs)
                return res
            else:
                print('鬼才知道你用的什麼認證方式')
                res = func(*args, **kwargs)
                return res

        return wrapper
    return auth_func

@auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一個auth_type  --->index=auth_func(index)
def index():
    print('歡迎來到京東主頁')

@auth(auth_type='ldap')
def home(name):
    print('歡迎回家%s' %name)
#
@auth(auth_type='sssssss')
def shopping_car(name):
    print('%s的購物車裏有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

# print('before-->',current_dic)
# index()
# print('after--->',current_dic)
# home('產品經理')
shopping_car('產品經理')
相關文章
相關標籤/搜索