首頁  >  文章  >  後端開發  >  裝飾器decorator詳解及實例

裝飾器decorator詳解及實例

高洛峰
高洛峰原創
2017-03-28 16:18:101620瀏覽

裝飾器

一、定義

1.裝飾者:本質是函數

2.功能:用來裝飾其他函數,為其他函數新增附加功能

二、原則

1.不能修改被裝飾函數的原始碼

2.不能修改被裝飾函數的呼叫方式

三、實作裝飾器

1.函數即變數 的概念

#2.高階函數

##3.巢狀函數

>> 高階函數+ 巢狀函數= 裝飾器

四、函數即變數

1、函數與變數的類比

x = 1
print(id(x))
def test():
	pass
print(test)
#输出
1842348496
<function></function>
在上例中我們定義了一個變數"x" 和一個函數test(),我們分別列印出變數和函數在記憶體中的位置。可以看出,print(test) 也就是 print("函數名稱")的時候,我們可以列印出函數的記憶體位址。

  我們看下面的程式碼:

def test():
	print("in the test.")
f=test
f()
#输出
in the test.
  我們把函數名稱test 賦予給f,然後運行f(),可以看出函數是可以正常運行的,而且就是test函數的運行結果。那麼這和下面的程式碼是不是類似:

x = 1
y = x
print(y)
#输出
1
  我們可以做出如下類比,函數名稱 test 相當於 x ,函數體就相當於 1,而 f 就相當於 y。

2.

python中記憶體的表現形式

    

裝飾器decorator詳解及實例

  我們把綠方塊當作是內存,每一個小方塊就是變數或函數在記憶體當中的位址。而

變數名稱(x)或函數名稱(test),我們可以形象的將他們比喻為門牌號碼。當需要呼叫變數或函數的時候,我們只要引用他們的門牌號碼就可以找到他們的記憶體位址並返回。只是函數的運算需要加(),如 test()。

  既然呼叫變數其實就是引用變數的記憶體位址,而呼叫函數名稱同樣可以得到函數體的記憶體位址。我們就可以把函數名稱當作變數名傳給函數,即函數就是變數。而將函數當作參數的函數,也就是高階函數。

五、高階函數

滿足下列條件之一就是高階函數

#1.把一個函數名稱當作實參傳給另一個函數

2.傳回值包含函數名稱

1)函數名稱作為參數

import time
def test1():
	time.sleep(2)
	print("in the test1.")
def test2(func):
	start_time = time.time()
	func()
	stop_time = time.time()
	print("The action time of program is {}".format(stop_time-start_time))
test2(test1)
#输出
in the test1.
The action time of program is 2.0012054443359375
  以上事例中,我們定義了一個函數test1,同時也定義了一個高階函數test2 。我們把test1函數名稱當作參數傳入test2中,可以實現這樣一個功能,為原本的test1函數增加了一個計算運行時間的功能。這有點像裝飾器了,但是有一點符合,就是上面的高階函數test2改變了函數的呼叫方式。

  但是我們實作了在不修改被裝飾函數的基礎上,新增了新功能。

2)傳回值中有函數名稱

def test1():
	time.sleep(2)
	print("in the test1.")
def test2(func):
	print(func)
	return func
test1 = test2(test1)
test1()
#输出
<function>
in the test1.</function>
  在上例中,我們最後將高階函數test2(test1) 賦予了test1,再次呼叫test1函數。我們可以直觀的看到test1函數的呼叫方式在此例中沒有改變。但是也並沒有增加新功能,而這就需要使用到巢狀函數了。

六、巢狀函數

在函數體內又是另一個函數的完整定義,這就是巢狀函數。

1)定義:

def foo():
	print("in the foo")
	def bar():
		print("in the bar")
	bar()
foo()
僅僅在函數內容呼叫函數,就不是巢狀函數,如下:

def test1():
    print("in the test1.")
def test2():
    test1()
2)巢狀函數的作用域

局部作用域和全域作用域的存取順序

x = 0
def grandpa():
	x = 1
	def dad():
		x = 2
		def son():
			x = 3
			print(x)
		son()
	dad()
grandpa()
#输出
3
3)使用巢狀函數為被修飾函數新增功能

  在高階函數第二例中,我們實現了不改變原函數的呼叫方式。而需要加入新功能的話,就要求修飾內容存在在回傳值中,也就是return func 中,我們可以定義一個巢狀函數來實現這個函數。

import time
def timer(func):  # timer(test1) func = test1
	def deco():
		start_time = time.time()
		func()   # run test1()
		stop_time = time.time()
		print("the action time of the program is {}".format(stop_time-start_time))
	return deco   # 返回了deco的内存地址
def test1():
	time.sleep(2)
	print("in the test1.")
test1 = timer(test1)
test1()
# 输出 
in the test1.
the action time of the program is 2.0003786087036133
  我們在timer()內部定義了一個巢狀函數 deco(),這個巢狀函數實作了為被修飾函數添加運行時間的函數。而timer()函數回傳了deco()的記憶體位址,這個記憶體位址deco就可以被引用,甚至直接賦予給 test1。這樣我們就可以直接執行 test1(),這樣就實作我們的裝飾器的功能。

七、裝飾器 

  python透過在函數定義前加上一個裝飾器名稱和@符號,來實現對函數的包裝 

import time
def timer(func):  # timer(test1) func = test1
	def deco():
		start_time = time.time()
		func()   # run test1()
		stop_time = time.time()
		print("the action time of the program is {}".format(stop_time-start_time))
	return deco   # 返回了deco的内存地址
@timer   # test1 = timer(test1)
def test1():
	time.sleep(2)
	print("in the test1.")
test1()

以上是裝飾器decorator詳解及實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn