Noah的學習筆記之Python篇:裝飾器

 

Noah的學習筆記之Python篇:html

  1.裝飾器編程

  2.函數「可變長參數」app

  3.命令行解析函數

  

注:本文全原創,做者:Noah Zhang  (http://www.cnblogs.com/noahzn/)學習

 

  年前工做事務比較繁瑣,我只能用零碎的時間繼續學習Python,決定開一個系列的博文,做爲本身深刻學習Python的記錄吧。名字也取好了,就叫《ZMAN的學習筆記之Python篇》~開篇是關於裝飾器的,春節假期碼的字哈哈~就讓咱們開始吧!spa

  本文的例子都是本身想的,若是不是很合適,請你們提出寶貴意見哈~謝謝啦!命令行

 

1、爲何要用「裝飾器」日誌

  好比咱們寫了以下一段代碼:code

# 打印0~99
def func():
    for i in range(100):
        print(i)

 

  咱們想要監測執行這個函數花費了多少時間,因而咱們將這個函數改爲了這樣:htm

 

import time


# 打印0~99
def func():
    start = time.time()
    for i in range(100):
        print(i)
    end = time.time()
    print("耗時:%.4f" % (end - start))

 

  雖然達到了目的,可是咱們卻改變了原有的函數,並且若是有幾十個不一樣的函數,都這樣改動一下,工做量也是很是大的。

  使用了「裝飾器」後,就能在不修改原函數的前提下,達到相同的功能。

 

2、什麼是「裝飾器」

  在Python中,函數是「對象」,而裝飾器是函數,它的做用就是對已經存在的函數進行裝飾。Python中的「裝飾器」能夠很好地解決那些有「面向切面編程」需求的問題。

  請看例子:

def deco(ex):
    print('func函數被調用前')
    ex()
    print('func函數被調用後')
    return ex

def func():
    print('func函數被調用')

func = deco(func)


>>> 
func函數被調用前
func函數被調用
func函數被調用後

 

  我寫了兩個函數,將函數func做爲參數傳入deco函數,並將返回值賦給func變量。咱們能夠當作是func函數通過了deco的裝飾~

  而這就是裝飾器的概念了:裝飾器能夠說是封裝器,讓咱們在被裝飾的函數以前或以後執行一些代碼,而沒必要修改函數自己。利用這點,咱們能夠作出許多酷炫的功能~

 

3、寫第一個「裝飾器」

  剛纔我介紹了裝飾器的概念,但這是一個「手工」的裝飾器,並無用到Python的裝飾器語法,實際上,裝飾器語法很是簡單,看例子:

def deco(ex):
    def _deco():
        print('func函數被調用前')
        ex()
        print('func函數被調用後')
    return _deco


@deco
def func():
    print('func函數被調用')
    #return('OK')


func()


>>> 
func函數被調用前
func函數被調用
func函數被調用後

 

  你們能夠看到,我在定義函數func的上一行,加了一句「@deco」,這就是裝飾器語法了,這樣寫了以後,能確保每次調用func函數都被deco函數裝飾,是否是很是簡單呀~~

 

4、讓被裝飾函數帶上肯定的參數

  若是被裝飾函數帶能夠肯定的參數,須要像下面這樣對裝飾器函數進行修改:

def deco(ex):
    def _deco(a, b):
        print('%s函數被調用前' % ex.__name__)
        c = ex(a, b)
        print('%s函數被調用後,結果爲:%s' % (ex.__name__, c))
        
    return _deco


@deco
def func(a, b):
    print('func函數被調用,傳入%s,%s' % (a, b))
    return a+b


func(1, 2)


>>> 
func函數被調用前
func函數被調用,傳入1,2
func函數被調用後,結果爲:3

 

  這個例子的裝飾器實現了:打印傳入函數的名字、打印兩個數字相加結果的功能。咱們在原先的deco函數內又定義了一個函數_deco用來接收func函數中的參數。

 

5、讓被裝飾函數帶上不肯定的參數

def deco(ex):
    def _deco(*args, **kwargs):
        print('%s函數被調用前' % ex.__name__)
        c = ex(*args, **kwargs)
        print('%s函數被調用後,結果爲%s' % (ex.__name__, c))

    return _deco


@deco
def func(a, b):
    print('func函數被調用,傳入%s,%s' % (a, b))
    return a+b


@deco
def func2(a, b, c):
    print('func2函數被調用,傳入%s,%s,%s' % (a, b, c))
    return a+b+c              


func(1, 2)
func2(1, 2, 3)


>>> 
func函數被調用前
func函數被調用,傳入1,2
func函數被調用後,結果爲3
func2函數被調用前
func2函數被調用,傳入1,2,3
func2函數被調用後,結果爲6

 

  簡單修改咱們的代碼,使用*args, **kwargs來捕捉不定量的傳參,便實現了多個參數的求和。

 

6、讓裝飾器帶上參數

def deco(ex):
    def _deco(func):
        def _deco2():
            print('%s函數被調用前,傳入參數爲:%s' % (func.__name__, ex))
            func()
            print('%s函數被調用後' % func.__name__)
        return _deco2
    return _deco

 
@deco('parameter1')
def func():
    print('func函數被調用')

 
func()


>>> 
func函數被調用前,傳入參數爲:parameter1
func函數被調用
func函數被調用後

 

  若是要讓裝飾器帶上參數,咱們要在裝飾器函數內部再多定義一層函數,用來接收裝飾器的參數~你們可不要搞混了裝飾器參數和函數的參數喲~

 

7、來個任性的:裝飾器和被裝飾函數都帶參數

def deco(ex):
    def _deco(func):
        def _deco2(c, d):
            print('%s函數被調用前,裝飾器參數爲:%s' % (func.__name__, ex))
            x = func(c, d)
            if x > 3:
                x = x-ex
            else:
                x = x+ex
            print('%s函數被調用後,計算結果爲:%d\n' % (func.__name__, x))
        return _deco2
    return _deco

 
@deco(3)
def func(a, b):
    print('func函數執行結果爲:%s' % int(a+b))
    return(a+b)

 
func(3, 4)
func(1, 2)


>>> 
func函數被調用前,裝飾器參數爲:3
func函數執行結果爲:7
func函數被調用後,計算結果爲:4

func函數被調用前,裝飾器參數爲:3
func函數執行結果爲:3
func函數被調用後,計算結果爲:6

 

  最初的func函數只是實現兩個數字的相加,通過裝飾後實現了對func返回的和的大小進行了分支處理:若是「兩數的和大於3」,最後結果爲「兩數的和減去3」,不然最後結果爲「兩數的和加上3」。我在這個例子中使用的是「肯定」的參數,你們能夠本身更改哦~

 

8、同時使用多個裝飾器

  以前的例子都是隻用了一個裝飾器,咱們固然能夠裝飾屢次啦~

def deco1(ex):
    def _deco1(string):
        print('deco1被調用前')
        string = ex(string)
        if 'hello' in string:
            string = "You are my old friend."
        else:
            string = "You are my new friend."
        print('deco1被調用後,%s\n' % string)

        return string
            
    return _deco1


def deco2(ex):
    def _deco2(string):
        print('deco2被調用前')
        string = ex(string)
        if 'ZMAN' in string:
            string = 'hello, ' + string
        else:
            string = 'Is your name ' + string + '?'
        print('deco2被調用後,%s' % string)

        return string

    return _deco2

@deco1
@deco2
def func(string):
    print('func函數被調用')
    return string

func('ZMAN')
deco1(deco2(func('John')))


>>> 
deco1被調用前
deco2被調用前
func函數被調用
deco2被調用後,hello, ZMAN
deco1被調用後,You are my old friend.

deco1被調用前
deco2被調用前
func函數被調用
deco2被調用後,Is your name John?
deco1被調用後,You are my new friend.

 

  在這個例子中,咱們主要要關注裝飾器調用的前後順序,此時func('ZMAN')和deco1(deco2(func('ZMAN')))是等同的,這個調用順序你們一看就明白了吧~

 

9、實際應用

  最後來個實際應用~好吧,我實在是絞盡腦汁了,寫代碼的時候正好在吃蘋果,那就來個跟水果有關的實例吧(別打我 - -!)

#綜合運用:簡單地檢測函數傳參是否合法

def deco(ex):
    def _deco(*args, **kwargs):
        print('***%s函數被調用前***' % ex.__name__)
        if args:
           if not isinstance(args[0], str):
               print('★店名參數錯誤:%s' % args[0])
           if not(isinstance(args[1], int) and args[1]>0):
               print('★員工參數錯誤:%s' % args[1])
               
        else:
            print('★未傳入店名和員工數信息!')

        if kwargs:
            for i in kwargs:
                if not ((isinstance(kwargs[i], int)
                    or isinstance(kwargs[i], float))
                    and kwargs[i]>0):
                    print('★水果單價參數錯誤:%s:%r' % (i, kwargs[i]))
        else:
            print('★未傳入水果單價信息!')

        a = ex(*args, **kwargs)
        print('***%s函數被調用後***\n' % ex.__name__)

    return _deco


@deco
# 假設傳入幾家水果店的名稱、員工數以及水果單價。店名爲字符,員工數爲正整數,單價爲正數
def func(*args, **kwargs):
    print('***函數func被調用***')
    brief = args
    detail = kwargs
    return(brief, detail)
                  

func('水果之家', -4, apple=3.5, strawberry=6, orange=3, cherry=8.5,)
func(123, 8, apple=3, orange=2,)
func('自然果園', 0.2, )
func()


>>> 
***func函數被調用前***
★員工參數錯誤:-4
***函數func被調用***
***func函數被調用後***

***func函數被調用前***
★店名參數錯誤:123
***函數func被調用***
***func函數被調用後***

***func函數被調用前***
★員工參數錯誤:0.2
★未傳入水果單價信息!
***函數func被調用***
***func函數被調用後***

***func函數被調用前***
★未傳入店名和員工數信息!
★未傳入水果單價信息!
***函數func被調用***
***func函數被調用後***

 

  代碼有點長,可是隻要你們耐心看,其實仍是挺簡單的,沒有什麼花裏胡哨的東西。這個裝飾器用來檢測傳參是否合法~

 

10、小結

  第一篇洋洋灑灑那麼多個例子,終於寫完了!利用「裝飾器」,咱們無須改寫原函數,就能對它進行功能擴充,好比計時、檢測傳參、記錄日誌等等。就好比咱們有一把槍,咱們能夠給它加上消音器,又或者是刺刀…不用的時候就拿掉,仍是原來的槍~~

  (本文不免有寫錯或不足的地方,但願你們不吝賜教哦~謝謝!)

相關文章
相關標籤/搜索