# 裝飾器形成的過程 : 最簡單的裝飾器 有返回值得 有一個參數 萬能參數
# 裝飾器的作用
# 原則 :開放封閉原則
# 語法糖:@裝飾函數名
# 裝飾器的固定模式
import time # time.time() # 獲取當前時間 # time.sleep() # 等待 # 裝飾帶參數的裝飾器 def timer(f): # 裝飾函數def inner(*args,**kwargs):start = time.time()ret = f(*args,**kwargs) # 被裝飾函數end = time.time()print(end-start)return retreturn inner @timer # 語法糖 @裝飾器函數名 在被裝飾函數的上面 def func(a): # 被裝飾的函數time.sleep(0.1)print('哈哈哈')return 'hello'# func = timer() ret = func() # inner print(ret)
?
# 裝飾器的作用 不想修改函數的調用方式 但是還想再原來的函數前后添加功能
# timmer 就是一個裝飾函數,只是對一個函數 有一些裝飾作用
# 原則:開放封閉原則
# 開放:對擴展是開放的
# 封閉:對修改是封閉的
# 裝飾器的固定模式
def wrapper(f): # 裝飾器函數,f是被裝飾的函數def inner(*args,**kwargs):'''在被裝飾函數之前要做的事'''ret = f(*args,**kwargs) # 被裝飾的函數'''在被裝飾函數之后要做的事'''return retreturn inner@wrapper # func_name = wrapper(func_name) def func_name():print(123)ret = func_name
?
裝飾器的進階
# functools.wraps
# 帶參數的裝飾器
# 多個裝飾器裝飾同一個函數
wrapsdef wahaha():'''打印這個函數'''pass print(wahaha.__name__) # 查看字符串格式函數名 print(wahaha.__doc__) # 查看函數注釋from functools import wraps # 引入wraps,全局被裝飾的函數也能使用 def wrapper(f):@wraps(f) # 在這里裝飾inner 作用保留原有函數的名稱和docstringdef inner(*args,**kwargs):'''在被裝飾函數之前要做的事'''ret = f(*args,**kwargs)'''在被裝飾函數之后要做的事'''return retreturn inner@wrapper # func_name = wrapper(func_name) def func_name():print(123)ret = func_nameprint(func_name.__name__)
# 帶參數的裝飾器
import time # 引入時間模塊 flage = False # 定義一個全局變量 def timer_out(flage): 裝飾器函數外面在裹一個函數def timer(func):def inner(*args,**kwargs):if flage: # 加個判斷start = time.time()ret = func(*args,**kwargs)end = time.time()print(end-start)return retelse:ret = func(*args,**kwargs)return retreturn innerreturn timer@timer_out(flage) #這里要明白 timer_out(flage) = timer ,所以還是@timer def wahaha():time.sleep(0.1)print('hehei')@timer_out(flage) def hel():time.sleep(0.1)print('lalala')wahaha() hel()
# 多個裝飾器裝飾一個函數
def wrapper1(func):def inner1():print('wrapperl,before func')func()print('wrapper1 after func')return inner1def wrapper2(func):def inner2():print('wrapper2,before func')func()print('wrapper2 after func')return inner2@wrapper1 @wrapper2 #距離最近的先執行,這個要好好理解執行變化 def f():print('in f') f()
# 1.編寫裝飾器,為多個函數加上認證的功能(用戶的賬戶密碼來源于文件)要求登錄成功一次,后續的函數都無需再輸入用戶名和密碼
flat = False #定義一個全局變量 好像其他也能代替 def login(func):def inner(*args,**kwargs):global flat # 這里不能用nonlocal聲明'''先登錄程序'''if flat:ret = func(*args,**kwargs)return retelse:username = input('username:')password = input('password:')if username == 'boss' and password == '666666':flat = Trueret = func(*args,**kwargs)return retelse:print('登錄失敗')return inner@login def shoplist_add():print('增加一件物品') @login def shoplist_del():print('刪除一件物品')shoplist_add() shoplist_del()
# 2.編寫裝飾器,為多個函數加上記錄調用功能,要求每次調用函數都將被調用的函數名稱寫入文件
def log(func):def inner(*args,**kwargs):with open('log','a',encoding='utf-8') as f:f.write(func.__name__+'\n')ret = func(*args,**kwargs)return retreturn inner@log def shoplist_add():print('增加一件物品') @log def shoplist_del():print('刪除一件物品')
?
# 進階作業(選做)
# 1.編寫下載網頁內容的函數,要求功能是:用戶傳入一個URL,函數返回下載頁面的結果
from urllib.request import urlopen def get(url):code = urlopen(url).readreturn code ret =get('http://www.baidu.com') print(ret)
#2.為題目1編寫裝飾器,實現緩存網頁內容的功能:
# 具體:實現下載的頁面存放于文件中,如果文件內有值(文件大小不為0),就優先從文件中讀取網頁內容,否則就去下載然后
import os from urllib.request import urlopen def cache(func):def inner(*args,**kwargs):if os.path.getsize('web_cache'):with open('web_cache','rb') as f:return f.read()ret = func(*args,**kwargs)with open('web_cache','wb') as f:f.write(ret)return retreturn inner@cache def get(url):code = urlopen(url).read()return coderet = get('http://www.baidu.com') print(ret)
?