python裝飾器

一、裝飾器如何使用

裝飾器是用來“打扮函數”的,在程序開發中常常使用到裝飾器,它使得開發效率更高,方便對代碼進行擴展。一般我們寫代碼時遵循開放封閉原則,它規定已經實現的功能代碼不允許被修改,但可以被擴展。
封閉: 已經實現的功能代碼塊不允許修改
開放:對擴展功能進行開發

"""現在需要對下面三個模塊進行功能擴展,在原來的基礎上加入驗證功能"""
def test1():
	print(‘--test1--’)

def test2():
	print('--test2--')

def test3():
	print('--test3--')

如果沒有裝飾器,那麼傳統的做法是先定義一個驗證模塊,然後每個模塊進行調用即可。下面看代碼

def verify():
	print("正在驗證......")
	if "條件":
		print("驗證成功!")
	else:
		print("驗證失敗,請確認!")
	
def test1():
	verify()
	print(‘--test1--’)

def test2():
    verify()
	print('--test2--')

def test3():
    verify()
	print('--test3--')

貌似利用上面的方法也可以實現。但是我們發現對原來的模塊進行了修改,違反了“開放封閉原則”。接下來採用一種方法來避免這種情況。就是利用閉包的手段來避免對原來模塊進行修改。

“”“驗證模塊設計”“”
def verify(func):
	def inner():
		print("正在驗證......")
		if "條件":
			print("驗證成功!")
			func() # 執行調用的函數
		else:
			print("驗證失敗,請確認!")
	return inner     # 返回函數inner()的引用

def test1():
	print(‘--test1--’)

def test2():
	print('--test2--')

def test3():
	print('--test3--')

# 下面分別爲函數test1()、test2()、test3()添加驗證功能
inner_fun = verify(test1)  # 這裏的test1參數是將函數test1的引用給了參數func。
inner_fun()   # 執行函數,上面參數傳入那個函數,調用內部函數時就執行相應的函數模塊。

# test2()中加入驗證功能
inner_fun = verify(test2)  # 這裏的test2參數是將函數test2的引用給了參數func。
inner_fun()   # 執行函數,上面參數傳入那個函數,調用內部函數時就執行相應的函數模塊

#test3()中加入驗證功能
inner_fun = verify(test2)  # 這裏的test2參數是將函數test2的引用給了參數func。
inner_fun()   # 執行函數,上面參數傳入那個函數,調用內部函數時就執行相應的函數模塊

利用閉包的方法似乎解決了上面對原來函數進行修改的問題。這樣滿足了開放封閉原則,但是似乎函數寫的不夠優美。下面利用裝飾器來簡化上述代碼。

“”“驗證模塊設計”“”
def verify(func):
	def inner():
		print("正在驗證......")
		if "條件":
			print("驗證成功!")
			func() # 執行調用的函數
		else:
			print("驗證失敗,請確認!")
	return inner     # 返回函數inner()的引用

@verify                 # 等價於:inner_fun = verify(test1)
def test1():
	print(‘--test1--’)

@verify                # 等價於:inner_fun = verify(test2)
def test2():
	print('--test2--')

@verify               # 等價於:inner_fun = verify(test3)
def test3():
	print('--test3--')
"""可以看到將上面代碼分別加入了@verify,這一句代碼表示的意思等價於: inner_fun = verify(test1)"""

那麼裝飾器函數在什麼時候進行裝飾? 只要上面一段代碼寫了@verify後,執行上面的代碼後,就進行了裝飾。我們會發現上面函數只是定義好了,但是還沒有調用函數怎麼會執行呢?這就時裝飾器的一個特點,只要定義了裝飾器 ,那麼在調用函數之前就進行了裝飾,而不是等到調用函數時才進行裝飾,這一點要清楚。

下面對函數進行調用:

# 調用test1
test1()

# 調用test2
test2()

# 調用test3
test3()
“”“其實我們發現比之前不用裝飾器時代碼更加簡潔。可以回過頭對比下之前沒用裝飾器時的代碼。””“

二、兩個裝飾器函數

# 定義裝飾函數
def makeBold(fn):
	def wrapped():
		return "<b>" + fn() + "</b>"
	return wrapped

# 定義裝飾函數
def makeItalic(fn):
	def wrapped():
		return "<i>" + fn() + "</i>"
	return wrapped
# @ makeBold 等價於 fn = makeBold(test)
@makeBold     # 後裝飾,先調用(執行)

# @ makeItalic 等價於 fn = makeItalic(test)
@makeItalic   # 先裝飾,後調用 (靠近函數的先裝飾,後調用)
def test():
	return "--兩個裝飾函數共同裝飾--" 	

# 調用函數test
test()
輸出結果:
<b><i> --兩個裝飾函數共同裝飾--<i><b>

三、帶有參數的裝飾器

裝飾器帶有參數,在原有的裝飾器的基礎上,設置外部變量。

# 下面的例子是裝飾器帶有參數的情況
from time import ctime,sleep
def timefun_arg(parse):
	def timefun(func):
		def wrappedfun():
			print("%s called %s %s"% (func.__name__,ctime(),parse))
		    return func
	    return wrappedfun
	return timefun

@timefun_arg("test") 
“”“先調用timefun_arg()這個函數,函數的返回值作爲裝飾,即timefun, 這裏相當於@ timefun,與上面講過的裝飾器是一樣的,只不過多了傳參數的這一步。”“”
def test1():
	print("---test1---")

@timefun_arg("python")
def test2():
	print("---test2---")

test1()   # 輸出結果:test1 called Mon Feb 10 08:48:00 2020 test
sleep(2)
test2()   # 輸出結果:test2 called Mon Feb 10 08:48:02 2020 python

那麼上面帶參數的裝飾器有什麼作用呢?如果我們只有一個裝飾器,讓它根據不同的參數進行特定的判斷,或者其他操作。將上面的列子加上判斷後看看效果。

from time import ctime,sleep
def timefun_arg(parse):
	def timefun(func):
		def wrappedfun():
		    if parse == "Python":
				print("This is %s"%parse)
			else: 
				print("Others")
		    return func
	    return wrappedfun
	return timefun

@timefun_arg("test") 
“”“先調用timefun_arg()這個函數,函數的返回值作爲裝飾,即timefun, 這裏相當於@ timefun,與上面講過的裝飾器是一樣的,只不過多了傳參數的這一步。”“”
def test1():
	print("---test1---")

@timefun_arg("Python")
def test2():
	print("---test2---")

# 調用函數
test1()   # 輸出結果:Others
sleep(2)  
test2()   # 輸出結果:This is Python
“”“可以通俗的說,帶參數的迭代器可以根據不同參數進行特定的作用!”“”
發佈了32 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章