python--裝飾器(附偏函數、斷言)

  博客地址:http://www.cnblogs.com/yudanqu/python

 

  概念:裝飾器是一個閉包,把一個函數當作參數返回一個替代版的函數,本質上就是一個返回函數的函數閉包

 

  裝飾器就是在咱們須要的一個函數外面包裝一個外殼,當咱們但願這個函數更漂亮時,能夠經過改變這個包裝的樣子便可,而不須要更改原函數,也能夠避免出錯。框架

一、簡單的裝飾器

def func2(func):
    def inner():
        print('************')
        func()
    return inner

def func1():
    print("this is one")

f = func2(func1)
f()  

  下面我解釋一下這個裝飾器,能夠看到,func1是咱們真正須要的函數,這時候咱們想在輸出這個函數時再多添加一些功能,那麼咱們就須要對func1這個函數進行裝飾。func2是一個外部的輸出函數,他的參數是func,這只是個形參,使用的時候將須要裝飾的函數傳入便可,當咱們執行func2這個函數的時候,其內部還有一個函數inner(),可是裏面inner只是建立了這個函數而沒有被調用(這個你們能理解嗎,就是咱們在定義一個函數時,他不會自動被調用,咱們使用他時須要人爲的經過這個函數來調用一下)內部讀取代碼僅讀到def inner()這裏並不進去,繼續向下就到了return inner,這裏返回了內部的那個函數名,但也只是個名字沒有被調用(函數名後面加上小括號才能夠調用)。那麼return返回給誰呢,就給了下面的f,用f來接收func2的return值,如今f就是return的inner。進行最後一步,調用inner,上一步咱們已經知道f就是inner,調用他就加個小括號就能夠了。調用了inner函數後,他的內部有func()這個函數,而且調用了,因此能夠直接執行func內的語句塊。func就是最外面傳進來的參數,也就是咱們須要被裝飾的函數。小夥伴們慢慢理一下思路,必定要把第一個理清楚了,那麼後面就垂手可得了。函數

二、裝飾器進階(含有一個參數)

def func4(func):
    def inner(age):
        if age < 0:
        age = 0
        func(age)
    return inner

def func3(age):
print("this is two %d" % age)

f = func4(func3)

f(-7)                

  看一下,這個框架和第一個幾乎同樣對吧,惟獨多了一個參數,這個參數是最後由咱們裝飾的那個函數來使用,由最外部傳入。咱們把參數給在了inner函數,這是爲何呢?咱們想一下,咱們裝飾的函數是在何時調用的,是否是在調用了inner函數後,inner函數內自動調用的啊。那麼在調用inner函數時才調用的目標函數,咱們不就應該把目標函數的參數從這裏傳進去嗎(最外層的函數是個包裝的做用,他是爲了返回inner函數的地址,來爲以後調用inner作準備),咱們在用f接收這個inner函數,咱們調用他還須要加個小括號f(),這纔是調用inner,既然這裏調用,正好有個傳入參數的接口,咱們趁着這個機會把想要實現的func3函數的參數扔進去,豈不美哉,接下來咱們執行func3函數的時候,只須要把上面接收到的參數再傳到本身的接口裏就行了。this

2二、改進

@func4
def func3(age):
    print("this is two %d" % age)
# 這時就能夠直接使用原來的func3函數了,不須要引入變量來接收,就已經可使用裝飾器裏的內容
func3(-7)

  @符號是裝飾器的語法糖,在定義函數時,能夠避免賦值操做spa

  那麼這段代碼有什麼做用呢?code

  其實他能夠替換掉上方代碼的後四行,代碼中最開始的裝飾器是同樣的,咱們改進在使用裝飾器上。上面咱們經過先定義一個目標函數,而後把他做爲參數傳入裝飾器裏,而後用一個變量f來接收,最終調用f()來實現裝飾做用,這樣是否是繁瑣了點呢,咱們作了這麼多工做,並且可能會把咱們搞暈,其中還引入了變量,當代碼很長時,那麼多的變量咱們怎麼記得住呢。這時就有了語法糖。大約在python2.4就開始可使用@符號了。也許你們見過,有時候會有@staticmethod,@classmethod的字眼,這就是python的內置裝飾器。blog

  @func4的做用就是替代了上方在裝飾器裏傳參數而後賦值的過程,能夠理解爲經過這樣一個符號,就已經把本身變成裝飾了以後的樣子,那麼咱們再使用的時候,只須要和以往同樣,調用函數,傳參就能夠了。固然是否是真正的變了,當下方再次使用這個函數時,若是不添加語法糖還會是函數本身。接口

 

  但此時的裝飾器只能接收一個參數,爲整形,由於內部使用了判斷,這樣的話咱們不少事情就不是很方便了,使用裝飾器就是方便咱們進行後續的操做,這樣的話咱們使用不同的功能還得老是修改裝飾器。那麼,咱們繼續向下看。字符串

三、進進階裝飾器(通用裝飾器)

def func5(func):
    def inner(*args,**kwargs):
        # 功能
        print("&&&&&&")
        func(*args,**kwargs)
    return inner

@func5
def func6(name,age,gender=1,number='00000000'):
    print('%s is %d years old,number is %s,gender:%d' % (name,age,number,gender))
func6('張三',18,0,'05162002')

# 函數的參數理論上是無限制的,但實際上最好不要超過6到7個    

  這個其實沒有太多要說的,只是把參數換成了不定長參數

    *args:能夠接受不限量個參數,將他們打包成tuple給函數

    **kwargs:能夠將關鍵字參數打包成字典給函數

  有了上面兩個,那麼幾乎全部的參數均可以隨便輸入了。

四、最後再順帶說一下偏函數和斷言,但這個不是重點:

 1 '''
 2 我理解爲偏函數就是能夠經過控制參數來實現功能
 3 '''
 4 
 5 # 這樣的一個功能,接下來實現它
 6 print(int('1010', base = 2))
 7 # base = 2 意思是把字符串當作二進制來計算,就是把這個字符串以二進制來判斷他是多少,以十進制輸出
 8 
 9 # 第一種方法
10 def int2(str, base = 2): # 表示設置默認值爲2.未來用base值來轉換
11     return int(str, base)
12 
13 # 第二種方法(偏函數)
14 import functools # 這個模塊幫咱們定義偏函數
15 int2 = functools.partial(int, base = 2)
 1 '''
 2 斷言
 3 '''
 4 
 5 def func(num, div):
 6     assert (div != 0), "div不能爲0" # 斷言
 7     return num/div
 8 
 9 func(10, 0)
10 # 函數自己分母不爲零,若是爲零那麼將報錯,使用斷言,當沒有錯誤時不產生效果,當有錯誤時會告訴你哪裏錯了

 

 

  做者:漁單渠(yudanqu)

  博客地址:http://www.cnblogs.com/yudanqu/

相關文章
相關標籤/搜索