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


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

 

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

 

  裝飾器就是在我們需要的一個函數外面包裝一個外殼,當我們希望這個函數更漂亮時,可以通過改變這個包裝的樣子即可,而不需要更改原函數,也可以避免出錯。

1、簡單的裝飾器

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就是最外面傳進來的參數,也就是我們需要被裝飾的函數。小伙伴們慢慢理一下思路,一定要把第一個理清楚了,那么后面就輕而易舉了。

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

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函數的時候,只需要把上面接收到的參數再傳到自己的接口里就好了。

22、改進

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

  @符號是裝飾器的語法糖,在定義函數時,可以避免賦值操作

  那么這段代碼有什么作用呢?

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

  @func4的作用就是替代了上方在裝飾器里傳參數然后賦值的過程,可以理解為通過這樣一個符號,就已經把自己變成裝飾了之后的樣子,那么我們再使用的時候,只需要和以往一樣,調用函數,傳參就可以了。當然是不是真正的變了,當下方再次使用這個函數時,如果不添加語法糖還會是函數自己。

 

  但此時的裝飾器只能接收一個參數,為整形,因為內部使用了判斷,這樣的話我們很多事情就不是很方便了,使用裝飾器就是方便我們進行后續的操作,這樣的話我們使用不一樣的功能還得總是修改裝飾器。那么,我們繼續向下看。

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

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:可以將關鍵字參數打包成字典給函數

  有了上面兩個,那么幾乎所有的參數都可以隨便輸入了。

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

 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/

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM