函數值之裝飾器

7.1 裝飾器

  本質就是一個python函數,它能夠在不改變函數任何代碼的前提下,增長函數額外的功能python

  例如:記錄日誌,性能測試,用戶登錄git

  裝飾器的返回值也是一個函數對象。微信

7.2 裝飾器造成

測試該函數執行時間:app

import time


def func1():
    print('in func1')


def timer(func):
    def inner():
        start_time = time.time()
        time.sleep(0.1)
        func()
        end_time = time.time()
        print('效率: %s' % (end_time - start_time))
    return inner


func1 = timer(func1)  
func1()         

 7.2.1 裝飾器的語法糖 @(基本語法)

import time
def timer(func):              #1 timer(func1),調用該函數
    def inner():              #3
        start = time.time()   #6
        func()                #7
        print(time.time() - start)  #9
    return inner              #4

@timer                        #2  func1 = timer(func1)
def func1():
    print('in func1')         #8
func1()                       #5

# 這一個函數自上而下,先找哪裏調用了它,先來到@timer,執行timer(func1),來到inner函數這裏,
# 但沒有調用它的方法(inner()),因此跳過這個函數直接到return inner(注意這裏return的是inner,
# 這個很關鍵,父級返回子級,可是沒有調用),接下來再從@timer如下去找哪裏還有調用函數,找到func1(),
# 如今func1()實際上是timer(func1)(),(不懂看下面這個小的例子,本身試試),外層函數執行完返回inner,
# 如今就是調用inner函數了,即timer(func1)() = inner().inner函數體內又包含一個函數的調用(func()),
# 此時的func()因爲timer(func1)這裏形參賦值變成了func1(),在#7 這裏對函數進行了調用,執行函數func1,
# 打印#8,最後打印#9。 
"""
def fun():
	a = 41
	def fun1():
		print(a, '啦啦啦')
	return fun1

fun()()
"""  

7.2.2 被裝飾函數帶有參數的裝飾器

import time
def timer(func):
    def inner(*args, **kwargs):
        start = time.time()
        re = func(*args, **kwargs)
        end = time.time()
        print(end - start)
        return re    # 我擦,又返回了這個func(*args, **kawrgs)
    return inner

@timer   # ==> jjj = timer(jjj)
def jjj(a):
    print('in jjj and get a:%s' % (a))
    return 'fun2 over'


jjj('aaaaaa')
print(jjj('aaaaaa'))    # 運行這步fun2 over 纔打印了

---in jjj and get a:aaaaaa
---0.0
---in jjj and get a:aaaaaa
---0.0
---fun2 over函數

 7.2.3帶有多個參數的裝飾器

def wrapper1(func):  
    def inner1():
        print('wrapper1 ,before func')  # 2
        func()  # 如今這裏變成了f() print('wrapper1 ,after func')  # 4
    return inner1

def wrapper2(func):  
    def inner2():
        print('wrapper2 ,before func')  # 1
        func()       # 如今這裏成了inner1() print('wrapper2 ,after func')  # 5
    return inner2
                            
@wrapper2  #  f = wrapper2(f)  裏面的f==inner1  外面的f == inner2
@wrapper1  # f = wrapper1(f)   裏面的f==函數名f  外面的f == inner1
# 即inner2 = wrapper2(wrapper1(f)),先執行裏面的wrapper1(f),return成了inner1,即inner2 = wrapper2(inner1),
執行wrapper2(inner1),return成了inner2,執行inner2函數,下來在執行inner1函數,在執行f()函數
def f(): # 3 print('in f') f()

---wrapper2 ,before func
---wrapper1 ,before func
---in f
---wrapper1 ,after func
---wrapper2 ,after func性能

# 優先執行靠近裝飾函數的語法糖,即wrapper1測試

 整個語法糖自上而下,先執行外層的wrapper2,wrapper1,spa

 在執行wrapper1下的函數體,再執行裏層的wrapper1,wrapper2日誌

7.2.4 帶參數的裝飾器

def outer(flag):   # outer(True)
    def timer(func):  # timer(func)
        def inner(*args, **kwargs):
            if flag:
                print('''執行函數以前要作的''')
            re = func(*args, **kwargs)     
            if flag:
                print('''執行函數以後要作的''')
            return re
        return inner 
    return timer      

@outer(True)  # func = outer(True)(func)
def func():
    print(111)

func()

# 這裏裝飾器所帶參數爲True,若是把它改成false則裝飾器不起裝飾做用,至關於去除了裝飾功能。

7.2.5通用裝飾公式

--------------------------記住了,這是固定套路---------------------------------
# 通用裝飾器 def wrapper(func): def inner(
*args, **kwargs): # 傳入萬能參數 '''執行函數前的操做''' ret = func(*args, **kwargs) '''執行函數後的操做''' return ret # 將函數的值返回 return inner # 添加裝飾器 @wrapper def func(): print('通用裝飾器') # 調用函數 func()

 7.3 裝飾器相關的擴展知識

import functools

def wrapper(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

@wrapper
def f1():
    print('f1')

@ wrapper
def f2():
    print('f2')


print(f1.__name__)      # f1
print(f2.__name__)      # f2

 關於functools模塊的使用,這裏因爲使用了@functools.wraps(func),原本二者打印的都是inner,可是因爲使用了它,輸出f1,f2code

 

做業題

1.寫函數,返回一個撲克牌列表,裏面有52項,每一項是一個元組

例如:[(‘紅心’,2),(‘草花’,2), …(‘黑桃’,‘A’)]

def cards():
	s = [(i, j) for i in ['黑', '紅', '梅', '方'] for j in range(1, 14)]
	return s

print(cards())


def cards(args):
	li = []
	for i in args:
		for j in range(1, 14):
			li.append((i, j))
	print(li)
cards(['黑', '紅', '梅', '方'])


標配呀,這個
def cards(*args):
	li = []
	for i in ['黑', '紅', '梅', '方']:
		for j in args:
			li.append((i, j))
	return li

print(cards(*(list(range(1, 11))), *['A', 'J', 'Q', 'K']))

2.寫函數,傳入n個數,返回字典{‘max’:最大值,’min’:最小值}

例如:min_max(2,5,7,8,4)

返回:{‘max’:8,’min’:2}

def min_max(*args):
	return dict([('max', max(args)), ('min', min(args))])

print(min_max(515,15,6,13,45))

3.寫函數,專門計算圖形的面積

其中嵌套函數,計算圓的面積,正方形的面積和長方形的面積

調用函數area(‘圓形’,圓半徑)  返回圓的面積

調用函數area(‘正方形’,邊長)  返回正方形的面積

調用函數area(‘長方形’,長,寬)  返回長方形的面積

def area():

      def 計算長方形面積():

           pass

      def 計算正方形面積():

           pass

      def 計算圓形面積():

           pass

def area(*args):
	def inner():
		if args[0] == '長方形':
			return args[1]*args[2]
		if args[0] == '正方形':
			return args[1]*args[1]
		if args[0] == '圓形':
			return args[1]*args[1]*3.14
	return inner()

print(area('圓形', 3))

4.寫函數,傳入一個參數n,返回n的階乘

例如:cal(7)

計算7*6*5*4*3*2*1

def cal(num):
	s = 1
	for i in range(num, 0, -1):
		s = s*i
	return s

print(cal(7))


def cal(num):
	if num == 1:
		return 1
	return num*cal(num-1)
print(cal(5))

5.給每一個函數寫一個記錄日誌的功能,

功能要求:每一次調用函數以前,要將函數名稱,時間節點記錄到log的日誌中。

所需模塊:

import time
struct_time = time.localtime()
print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))

import time
def func(func2):
	def inner():
		struct_time = time.localtime()
		with open('log.txt', mode='a', encoding='utf-8') as f:
			f.write('{}\t{}\n'.format(func2.__name__,
					 time.strftime("%Y-%m-%d %H:%M:%S", struct_time)))
		func2()
	return inner

@func
def func1():
	pass
func1() 

六、編寫裝飾器,爲多個函數加上認證的功能(用戶的帳號密碼來源於文件),要求登陸成功一次,後續的函數都無需再輸入用戶名和密碼

dic = {
    'username':None,
    'status':False,
}

def wrapper(func):
    def inner(*args, **kwargs):
        if dic['status']:
        # 開始是False,因此先走else,用戶帳戶密碼正確下,這裏改成True,就一直執行這步
            ret = func(*args, **kwargs)
            return ret
        else:
            i = 0
            while i < 3:
                username = input('請輸入用戶名:').strip()
                password = input('請輸入密碼:').strip()
                with open('register_msg', encoding='utf-8') as f1:
                    for j in f1:
                        j_li = j.strip().split()  # ['張三','123']
                        if username == j_li[0] and password == j_li[1]:
                            dic['username'] = username
                            dic['status'] = True
                            ret = func(*args, **kwargs)
                            return ret
                    else:   # 注意這裏的else縮進,若是在if下面則會循環打印
                        print('帳號或者密碼錯誤,請從新輸入%s機會' % (2-i))
                        i += 1

    return inner


@wrapper
def article():
    print('文章')

@wrapper
def diary():
    print('日記')

@wrapper
def comment():
    print('評論')

@wrapper
def file():
    print('文件')


article()
diary()
comment()
file()

7.在編寫裝飾器,爲多個函數加上認證的功能(用戶的帳號密碼來源於文件),要求登陸成功一次,後續的函數都無需再輸入用戶名和密碼。這個做業之上進行升級操做:

      設置兩套密碼,一套爲京東帳號密碼,一套爲淘寶帳號密碼保存在文件中。

      設置四個函數,分別表明 京東首頁,京東超市,淘寶首頁,淘寶超市。

      循環打印四個選項:東首頁,京東超市,淘寶首頁,淘寶超市。

      供用戶選擇,用戶輸入選項後,執行該函數,四個函數都加上認證功能,只要登錄成功一次,在選擇其餘函數,後續都無需輸入用戶名和密碼。

相關提示:用帶參數的裝飾器。裝飾器內部加入判斷,驗證不一樣的帳戶密碼。

# user_pwd文件內容: {'微信':{'username':'老男孩', 'password': '123'},
#               'qq':{'username':'老男孩1', 'password': '123'},}
dic = {
    'username':None,
    'status':False,
}


def login(flag):
    def wrapper(func):
        def inner(*args, **kwargs):
            if dic['status']:
                ret = func(*args, **kwargs)
                return ret
            else:
                i = 0
                while i < 3:
                    username = input('請輸入用戶名(用%s帳號):' % flag).strip()
                    password = input('請輸入密碼:').strip()
                    with open('user_pwd',encoding='utf-8') as f1:
				        msg_dic = eval(f1.readline())  # 經過eval函數強制性將字符串轉換成了字典
                        # {'微信': {'password': '123', 'username': '老男孩'},
                        # 'qq': {'password': '123', 'username': '老男孩1'}}
                        if username == msg_dic[flag]['username'] and password == msg_dic[flag]['password']:
                            dic['username'] = username
                            dic['status'] = True
                            ret = func(*args, **kwargs)
                            return ret
                        else:
                            print('您輸入的用戶或者密碼錯誤,請從新輸入,還有%s次機會' % (2-i))
                            i += 1
        return inner
    return wrapper




@login('微信')
def taobao_home():
    print('淘寶首頁')

@login('微信')
def taobao_shop():
    print('淘寶超市')

@login('qq')
def jingdong_home():
    print('京東首頁')

@login('qq')
def jingdong_shop():
    print('京東超市')

choice_dict = {
    1: taobao_home,
    2: taobao_shop,
    3: jingdong_home,
    4: jingdong_shop,
}

while True:
    print('1 淘寶首頁\n2 淘寶超市\n3 京東首頁\n4 京東超市')
    choice_num = input('請選擇輸入的序號:').strip()
    if choice_num.isdigit():
        choice_num = int(choice_num)
        if 0 < choice_num <= len(choice_dict):
            choice_dict[choice_num]()
        else:
            print('請輸入範圍內的序號')
    else:
        print('您輸入的有非法字符,請從新輸入')
相關文章
相關標籤/搜索