天天看點

裝飾器

裝飾器

在不改變被裝飾對象"内部代碼"以及"調用方式"的基礎上添加新的功能
開放封閉原則
	對擴充開放
	對修改封閉
裝飾器原則:讓使用者察覺不到程式被增加
           

嘗試添加裝飾器

1.原來的程式
def old():  # 建立函數
	print('假裝這是代碼!')  # 函數體代碼
	return 123  # 傳回值
old()
假裝這是代碼!




2.新加入的功能
# 添加新的功能,不能更改原來的函數體代碼和調用方式
print('新的代碼!')  # 1.直接添加新的功能
old()  # 2.運作函數體代碼
print('又是一個新的代碼!')  # 3.添加另一個功能
新的代碼!
假裝這是代碼!
又是一個新的代碼!
#可以運作,但如果需要多個位置需要添加同樣的功能就會繁瑣
#解決需要重複的問題




3.解決代碼需要重複的問題
def new():  # 将上一個代碼封裝成函數,可以在任意位置調用
	print('新的代碼!')
	old()
	print('又是一個新的代碼!')
new()
新的代碼!
假裝這是代碼!
又是一個新的代碼!
# 問題,隻可以運作相同的代碼,沒有實際意義,需要靈活運用
#想辦法靈活的将新代碼添加到其他代碼之上




def old():
	print('假裝這是代碼!')
	return 123
def old_1():  # 1。另一個需要同樣功能的代碼
	print('另一個需要新增功能的代碼!')
def new(a):  # 2.函數調整為位置形參
	print('新的代碼!')
	a()  # 4.傳入的實參直接代替a,也就是函數加上括号就會運作
	print('又是一個新的代碼!')
#可以運作,但如果需要多個位置需要添加同樣的功能就會繁瑣
# 函數封裝可以在任意位置使用
new(old)  # 3.調用new并将old以實參輸入,
new(old_1)  # 3.1調用new并将old_1以實參輸入,
新的代碼!
假裝這是代碼!
又是一個新的代碼!
新的代碼!
另一個需要新增功能的代碼!
又是一個新的代碼!
#解決了代碼不能靈活運用的問題,但更改了原來的傳參方式
#需要解決傳參的問題



def old():
	print('假裝這是代碼!')
	return 123
def old_1():
	print('另一個需要新增功能的代碼!')
def new_1():  # 1.建立一個閉包函數
	#  但是将這個寫死了,沒辦法改變 a=old,
	a = old  # 2.将old直接指派給下面的a。然後運作
	def new():  # 3.去掉new需要的形參
		print('新的代碼!')
		a()  # 4.a被old指派,運作到這裡會自動找到old
		print('又是一個新的代碼!')
	return new  # 5.new_1傳回的值為new
res = new_1()  # 6.将傳回來的值指派給res
res()  # 7.傳回來的new加上括号就會直接運作函數體代碼
# 更改了傳參方式,添加閉包函數




def old():
	print('假裝這是代碼!')
	return 123
def old_1():
	print('另一個需要新增功能的代碼!')
def new_1(a):   # 1.為了防止代碼不被寫死,将new_1調整為需要個實參才可以,填入的這個參數就是需要運作的old或者old_1都可以
	def new():  
		print('新的代碼!')
		a()  # 2.這裡的a就是傳入的參數
		print('又是一個新的代碼!')
	return new  
old= new_1(old)  #3.關鍵
old()
# 沒有修改原代碼,沒有改變傳參方式
但如果給其他函數添加同樣的功能需要重新填寫指派等号
old_1 = new_1(old_1)
old_1()
# 那麼有沒有辦法可以在友善一些呢,或者可以添加有參函數呢
'''
關鍵:代碼運作到old=new_1(old)會先運作new_1()的函數,并将old以實參的方式傳遞進去,此時a=old的記憶體位址,new_1的傳回值時new指派old。(原函數名old記憶體位址已經被a接收,old這個變量名現在可以随時指向新的記憶體位址)然後old()是運作new的函數體代碼,old指向的就是new。運作new時a就會使用原old的記憶體位址(也就是運作old函數體代碼)
'''
           

裝飾器繁瑣

def old():  # 1.函數old需要參數,調用需要參數
	print('假裝這是代碼!')
	return 123
def old_1(b):
	print('另一個需要新增功能的代碼!')
def new_1(a):
	def new(*args,**kwargs):  # 1.new需要位置參數或者關鍵字參數都可以運作
		print('新的代碼!')
		a(*args,**kwargs)  # 2.傳入任何參數都可以使用或者不傳入參數
		print('又是一個新的代碼!')
	return new
old = new_1(old)
old()
old_1 = new_1(old_1)
old_1(1)  # 3.最後需要運作的函數需要參數是以當把new傳回的時候填入參數,現在old_1就是new。運作到a的時候填入的參數會自動填入
# 完全實作了可以給無參或者有參添加新功能
但沒有傳回值
           

裝飾器傳回值

def old():
	print('假裝這是代碼!')
	return 123
def old_1(b):
	print('另一個需要新增功能的代碼!')
	return 456
def new_1(a):
	def new(*args,**kwargs):
		print('新的代碼!')
		l1 = a(*args,**kwargs)  # 1.将原代碼的傳回值設定變量名,接受傳回值
		print('又是一個新的代碼!')
		return l1  #2.将傳回值傳回出去
	return new
old = new_1(old)
l2=old()  # 3.将傳回值接收
old_1 = new_1(old_1)
l3=old_1(1)  # 3.1将傳回值接收
print(l2)  #列印
print(l3)  #列印
           

登入認證

def add():
	print('這是111')
def app():
	print('這是222')
def xpp(a):  #形參不可以更改
	def all(*args,**kwargs):  #形參不可以更改
		name = input('賬号').strip()
		pasd = input('密碼').strip()
		if name == 'x'and pasd == '123':
			i1 = a(*args,**kwargs)
			return i1
		else:
			print('輸入錯誤!')
		return
	return all
add = xpp(add)
i2=add()
print()

           

一次性登入認證

def add():
	print('這是111')
def app():
	print('這是222')
dict_1 = {'1':False}  # 類似功能字典
def xpp(a):
	def all(*args,**kwargs):
		if dict_1.get('1'):  # 判斷字典内是False或者True
			i1 = a(*args, **kwargs)
			return i1
		name = input('賬号').strip()
		pasd = input('密碼').strip()
		if name == 'x'and pasd == '123':
			i1 = a(*args,**kwargs)
			dict_1['1'] = True  # 登入成功将False改成True
			return i1
		else:
			print('輸入錯誤!')
	return all
add = xpp(add)
i2=add()
print()
           

裝飾器模闆

def outer(func):
	def inner(*args,**kwargs):
		print('執行函數之前添加的功能')
		res = func(*args,**kwargs)  #執行被裝飾的函數
		print('執行函數之後添加的功能')
		return res #被裝飾的函數傳回值的傳回
	return inner
def index (*args,**kwargss)
	print('這是文法糖')
inder = outer(inder)
           

文法糖

def outer(func):
	def inner(*args,**kwargs):
		print('執行函數之前添加的功能')
		res = func(*args,**kwargs)  #執行被裝飾的函數
		print('執行函數之後添加的功能')
		return res #被裝飾的函數傳回值的傳回
	return inner
def index (*args,**kwargss)
	print('這是文法糖')
inder = outer(inder)
@outer  #  inder = outer(inder)  相等
裝飾器文法糖書寫規範
	文法糖必須緊貼着要裝飾的對象上方
裝飾器文法糖内部原理
	自動将下面緊貼着的被裝飾對象名字當做參數傳給裝飾器函數調用
           

雙層文法糖

import time
def get_time(func):
	def inner(*args, **kwargs):
		start_time = time.time()
		res = func(*args, **kwargs)  # 執行被裝飾的函數
		end_time = time.time()
		print('函數執行時間:%s'%(end_time-start_time))
		return res  # 将被裝飾函數執行之後的傳回值傳回
	return inner
# 校驗使用者登入裝飾
def login_auth(func):
	def inner(*args, **kwargs):
		# 1.先擷取使用者的使用者名和密碼
		username = input('username>>>:').strip()
 		password = input('password>>>:').strip()
 		# 2.校驗使用者名和密碼是否正确
 		if username == 'jason' and password == '123':
 			res = func(*args, **kwargs)  # 執行被裝飾的函數
			return res  # 将被裝飾函數執行之後的傳回值傳回
		print('使用者名或密碼錯誤 無權限執行')
	return inner
@login_auth
@get_time
def index():
    time.sleep(1)
    print('from index')
index()
'''
先執行距離函數體最近的文法糖,之行結束的傳回值會被第二層文法糖運用
'''
           

裝飾器修複技術

from functools import wraps
def outer(func):
	@wraps(func)  # 修複技術就是為了讓被裝飾對象更加不容易被察覺裝飾了
	def inner(*args, **kwargs):
		print('執行函數之前可以添加的額外功能')
 		res = func(*args, **kwargs)  # 執行被裝飾的函數
		print('執行函數之後可以添加的額外功能')
 		return res  # 将被裝飾函數執行之後的傳回值傳回
	return inner


@outer  # index = outer(index)
def index():
	print('from index')
print(index)
help(index)

def home():
	"""這是一個home函數"""
	print('from home')
# help(index)
# help(home)
# print(index)
# help(len)
help()  #檢視函數參數
from functools import wraps
def 函數名():
	@wraps(函數名)	
				'''就這麼用,别問為什麼'''
           

練習題

def outter1(func1):
	print('加載了outter1')
	def wrapper1(*args, **kwargs):
		print('執行了wrapper1')
		res1 = func1(*args, **kwargs)
		return res1
	return wrapper1
def outter2(func2):
	print('加載了outter2')
	def wrapper2(*args, **kwargs):
		print('執行了wrapper2')
		res2 = func2(*args, **kwargs)
		return res2
	return wrapper2
def outter3(func3):
	print('加載了outter3')
	def wrapper3(*args, **kwargs):
		print('執行了wrapper3')
 		res3 = func3(*args, **kwargs)
		return res3
	return wrapper3
@outter1
@outter2
@outter3
def index():
    print('from index')
七個print的列印順序
           

有參裝飾器

def outer(source_data):
	# source_data = 'file'
	def login_auth(func):
		def auth(*args,**kwargs):
			# 2.校驗使用者名和密碼是否正确
			# 資料的校驗方式可以切換多種
			if source_data == 'file':
				# 從檔案中擷取使用者資料并比對
				print('file檔案擷取')	
			elif source_data == 'MySQL':
  				# 從MySQL資料庫中擷取資料比對
				print('MySQL資料庫擷取')
			elif source_data == 'postgreSQL':
				# 從postgreSQL資料庫中擷取資料對比
				print('postgreSQL資料庫擷取')
			else:
				print('使用者名或密碼錯誤 無法執行函數')
		return auth
	return login_auth

@outer('file')
def index():
	print('from index')
@outer('MySQL')
def home():
	print('from home')

index()
home()
#有參函數,最外層函數在包一層