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、小結
第一篇洋洋灑灑那麼多個例子,終於寫完了!利用「裝飾器」,咱們無須改寫原函數,就能對它進行功能擴充,好比計時、檢測傳參、記錄日誌等等。就好比咱們有一把槍,咱們能夠給它加上消音器,又或者是刺刀…不用的時候就拿掉,仍是原來的槍~~
(本文不免有寫錯或不足的地方,但願你們不吝賜教哦~謝謝!)