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