Python中的裝飾器是通過利用了函數特性的閉包實作的,是以在說裝飾器之前,我們需要先了解函數特性,以及閉包是怎麼利用了函數特性的。
1、函數特性
Python中的函數特性總的來說有四點:
(1)函數作為變量傳遞
def add(x):
return x + 1
a = add
(2)函數作為參數傳遞
def add(x):
return x + 1
def excute(f):
return f(3)
excute(add)
(3)函數作為傳回值
def add(x):
return x + 1
def get_add():
return add
(4)函數嵌套及跨域通路
def outer():
x = 1
def inner():
print(x)
inner()
outer()
2、閉包的實作
Python中的裝飾器是通過閉包實作的,
簡單地講,閉包就是引用了外部變量的内部函數
,而閉包的實作正是利用了以上函數特性,下面我們來看看閉包是如何實作的:
def outer(x):
def inner(): # 函數嵌套
return x # 跨域通路,引用了外部變量x
return inner # 函數作為傳回值
closure = outer('外部變量') # 函數作為變量賦給closure
print(closure()) # 執行閉包
#結果
外部變量
在這個流程中,outer接收到’外部變量’,傳給inner,作為它return的參數,最後outer傳回inner函數,傳回的inner函數作為變量傳遞給closure,最後執行closure這個函數對象,實際上是執行了inner這個函數,傳回了 ‘外部變量’,這樣就實作了一個簡單的閉包。
上面這個閉包例子隻用到了之前說的三個函數特性,函數作為參數這個特性好像并沒有用上,下面做一下延伸,把outer的參數x用一個函數對象替代。
def func():
return '函數func'
def outer(x):
def inner(): # 函數嵌套
return '戴了inner牌帽子的 ' + x() # 跨域通路,引用了外部變量x
return inner # 函數作為傳回值
closure = outer(func) # 函數func作為outer的參數;函數作為變量賦給closure
print(func()) # 執行原始函數
print(closure()) # 執行閉包
# 結果
函數func
戴了inner牌帽子的 函數func
觀察上面的例子,從func()到closure(),函數func就是被裝飾了一番變成了clousre,分析一下具體過程:
closure實際上是outer(func),func作為參數傳進outer,outer的子函數inner對func傳回的結果進行了一番裝飾,傳回了一個裝飾後的結果,最後outer傳回inner,可以說inner就是裝飾後的func,這就是一個函數被裝飾的過程,重點在于執行 outer(func) 這個步驟。
3、裝飾器文法 糖@
Python給我們提供了文法糖 @,我們想執行 outer(func) 的時候,隻需要把outer函數@到func函數的上面就可以了。
def outer(x):
def inner():
return '戴了inner牌帽子的 ' + x()
return inner
@outer
def func():
return '函數func'
print(func())
#結果
戴了inner牌帽子的 函數func
列印的結果跟我們執行closure()的結果是一樣的,也就說明 加了outer裝飾器的func 等價于 outer(func),是以我們很清楚地知道裝飾器@的作用是什麼了,就是拿來把被裝飾的函數作為參數傳遞到裝飾器函數裡面加工的,最後執行被裝飾函數的時候,就相當于執行了一個加工後的函數。
以上就是Python中裝飾器的實作原理。
4、裝飾器的使用
Python中裝飾器用處很多,下面寫一個例子感受一下.
類中的私有屬性在類外部是無法通路的,而這時可以在類中定義一個方法傳回這個私有屬性然後在外部調用就可以得到這個私有屬性,但是這樣看起來就和正常的調用屬性的方式不一樣了(obj.屬性),這時候就可以用@property來實作想要的效果.
class Person:
def __init__(self,name,age):
self.name = name
if type(age) is int:
self.__age = age
else:
print( '你輸入的年齡的類型有誤,請輸入數字')
@property
def age(self):
return self.__age
@age.setter
def age(self,a):
'''判斷,你修改的年齡必須是數字'''
if type(a) is int:
self.__age = a
else:
print('你輸入的年齡的類型有誤,請輸入數字')
@age.deleter
def age(self):
del self.__age
p1 = Person('帥哥',20)
print(p1.age)
del p1.age