python學習筆記之函數總結--高階函數以及裝飾器
Python特點:
1、不是純函數式編程(允許變量存在);
2、支持高階函數(可以傳入函數作爲變量);
3、支持閉包(可以返回函數);
4、有限度的支持匿名函數;
高階函數:
1、變量可以指向函數;
2、函數的參數可以接收變量;
3、一個函數可以接收另一個函數作爲參數;
下面我將示例一些函數的寫法以及使用,並說明python中函數的特性:
1、基本的高階函數示例:
#!/usr/bin/env python def func(): print "ext_func" def inn_func(): print "inn_func" return inn_func() print func()
在func函數中我們首先輸出的是ext_func表明這是在外部函數中輸出的語句,隨後在裏面定義了內置函數inn_func,在內部函數中輸出的是inn_func,最後在外部函數中return返回內部函數的結果
下面是程序運行的結果:
ext_func inn_func None
運行程序,首先調用外部函數func,輸出語句ext_func,然後return內部函數,內部函數輸出inn_func,函數func調用已經完成,返回結果爲inn_func的返回值,其返回值爲None,我在最後調用時,是print fun(),即打印func函數的返回值,它返回inn_func的返回值,即爲None.
2、我們在上一個程序的基礎上,進行改進,在調用時如下:
#!/usr/bin/env python def func(): print "ext_func" def inn_func(): print "inn_func" return inn_func f = func() f()
我們現將func()的返回值傳遞給變量f,再調用f函數
結果如下:
ext_func inn_func
通過結果,可以看出,第一次是將func()的返回值賦給了f,而func()的返回值就是return inn_func,即把inn_func這個內部函數傳遞給了變量f,所以在後面調用f()時,就相當於調用函數inn_func(),所以只有兩個print 語句輸出了。
3、下面是一個對部函數的改進,調用了兩個模塊time和datetime,簡單地實現了一個計時:
#!/usr/bin/env python import time import datetime def func(): print "ext_func" def inn_func(f): start = datetime.datetime.now() f() end = datetime.datetime.now() cost = end - start print cost.total_seconds() return inn_func def f2(): time.sleep(3) f = func() f(f2)
在上面的函數體中,我們對內部函數進行了改進,其中start和end分別是獲取當前系統時間,中間是調用了一個函數f,所以end和start的差值就是函數f執行的時間cost,爲了顯示的是秒,我們對cost做了一個轉化爲秒數的改動,即調用total_seconds方法。
下面是程序執行後的結果:
ext_func 3.004226
首先將func函數結果傳遞給變量f的時候,打印了一句ext_func語句,這時函數f其實就是inn_func函數,因爲func函數的返回值就是inn_func函數;調用f函數,這時我們要傳遞一個參數進去,而這個參數還是另一個函數,我們傳遞的是自定義的f2函數,它sleep了3秒,所以兩個查找cost也是3秒,和預期效果一樣。
特別要注意的是,調用一個函數的內部函數,有可能是需要傳遞參數進去了,可能在外部函數傳遞參數,也可能是在內部函數進行傳遞參數,一定要注意到底在哪塊傳遞參數,傳的參數是什麼,在裏面哪裏調用了,以及在最後使用函數時,如何根據所寫的不同,要具體怎樣去調用函數。
4、下面在看一個改進後的程序示例:
#!/usr/bin/env python import time import datetime def func(fc): print "ext_func" def inn_func(*arg,**kwargs): start = datetime.datetime.now() fc(*arg,**kwargs) #f2(arg) end = datetime.datetime.now() cost = end - start print cost.total_seconds() return inn_func def f2(arg): print arg time.sleep(arg) def f3(arg1,arg2): print arg1,arg2 time.sleep(5) def f4(): time.sleep(6) f = func(f2) f(4)
在這個程序中,我們傳遞了參數,首先是對內部函數的參數傳遞,有時會傳遞進來一個函數在裏面調用,而傳遞進來的函數有可能也是需要傳遞參數的,所以我們就要處理傳遞函數和傳遞函數參數的問題,一般我們將函數在外部函數中進行傳遞進來在裏面進行調用,而函數所需要的參數,我們可以在內部函數中進行傳遞,比如上面這個程序,外部函數傳遞進來的fc在內部函數中進行調用,而傳進來函數的參數在內部函數中進行傳遞;
爲了能將所有情況下的參數全部傳遞進來,所以在內部函數傳參時,採用了*arg和**kwargs以保證所有類型的傳參都能成功,在具體fc函數調用時,則需要進行解序列傳參進去使用,最後的幾個函數就是爲了進行測試不同參數傳參的問題。
下面是程序運行的結果:
ext_func 4 4.005157
可以看到傳遞了f2函數給外部函數func,將其返回值inn_func傳遞給f,調用f函數時,還需要傳遞一個參數進去,這是根據f2函數內部結構得到的,傳遞的值是4,所以結果和預期一致。
5、下面是一個練習腳本示例:
#!/usr/bin/env python def func1(fc): print "This is a function of boss." print type(fc) def func2(*arg): print "This is a function in function." print arg fc(*arg) print fc(*arg) return func2 def f1(x,y): return x*y f = func1(f1) f(3,5)
結果如下:
This is a function of boss. <type 'function'> This is a function in function. (3, 5) 15
6、裝飾器的一些示例與作用說明:
#!/usr/bin/env python import time import datetime def func(fc): print "ext_func" def inn_func(*arg): start = datetime.datetime.now() fc(*arg) #f2(arg) end = datetime.datetime.now() cost = end - start print cost.total_seconds() return inn_func @func def f2(arg): print arg time.sleep(arg) @func def f3(arg1,arg2): print arg1,arg2 time.sleep(5) @func def f4(): time.sleep(6) #f = func(f4) #f() #f2(3) #fc = func(f2);fc(3) f3(3,5)
在這段程序中,主要是使用了裝飾器,即@func就是下面定義函數的裝飾器
在執行的時候直接調用要傳遞的參數就可以實現和上面一樣的功能,這是因爲裝飾器解釋給解釋器,自動執行了原來需要手動傳遞函數的那一步,即fc = func(f2);所以你執行了f2(3),就相當於執行了fc =func(f2); f2(3)兩步。
注意:裝飾器要在函數定義前進行裝飾,每裝飾一個函數,解釋器都會執行fc = func(f2)這一步,在下面的結果處你就可以看到,每個解釋器只能解釋一個函數,相同的需要都寫在前面。
下面是程序執行的結果:
ext_func ext_func ext_func 3 5 5.005236
可以看到,在程序中值調用了一次函數,就是f3(),可是ext_func打印了三遍,這是因爲裝飾器的原因,自動執行了一步,但並沒有執行後面的內部函數調用,所以只打印ext_func這句話,而f3()則進行了調用,且傳遞了參數爲5,所以打印結果是時間。和預期效果一致。
7、練習示例:
#!/usr/bin/env python def check_priv(fc): defwrap(*arg,**kwargs): ifusername == 'admin': fc(*arg,**kwargs) else: print"Permission denied" returnwrap username = "admin" @check_priv def f6(arg): printarg print"restart nginx...",arg f6('ok')
結果如下:
ok restart nginx... ok
8、裝飾器結合functools返回函數自身屬性(函數名):
#!/usr/bin/env python import functools def func_boss(func): print "This is a function boss" @functools.wraps(func) def func_son(*arg,**kwargs): print "This is a functionson" func(*arg) print "Ending" return func_son S = 1 @func_boss #func_test = func_boss(func_test) def func_test(*arg): print "This is a function test" #S = 1 global S for i in range(len(arg)): S = S*arg[i] print S return S #func_test(1,2,3,4) #func_test() a = func_test print a.__name__
在上面的程序中,我們看到func_boss作爲裝飾器裝飾了函數func_test,這樣等於是執行了func_test =func_boss(func_test)這一步,這我們都在上面進行了說明;
那麼如果我調用函數func_test時,如果要獲取它的名字,返回的結果是什麼呢,通過測試發現它返回的是內部函數func_son,這並不是我們想要的結果,因爲執行了裝飾器的功能,所以函數的屬性已經變成了內部函數func_son的,因爲func_test就是接受了裝飾器的結果,即return func_son。
這時,如果我們想要原來函數的屬性的話,可以藉助functools模塊,首先應該導入這個模塊,在裝飾器函數裏面進行調用,這裏是在內部函數func_son定義之前加一個解釋器@functools.wraps(func),它的功能是將func函數的屬性賦給高階函數中的內部函數(又稱爲wrap函數),這樣執行的結果就是我們想要的。
不加@functools.wraps(func)的結果如下所示:
This is a function boss func_son
加@functools.wraps(func)的結果如下:
This is a function boss func_test
和我們想要的結果一樣,獲取到的是func_test函數名
9、yield的用法及示例(一):
!/usr/bin/env python def odd(): n= 1 print"hello yield" whileTrue: #print"before yield" yieldn print"after yield" n+=2 for i in odd(): printi ifi >= 101: break
yield出現的函數就會有一個迭代器,它返回的值是依次取的,當你需要用的時候纔會給你,不會馬上把所有的值都取完,這樣太費內存,浪費資源,yield就是返回一個值就會停住,進行等待,當你下一次需要值時,它才執行去返回下一個值,否則就一直停在那,這樣做的好處是節省資源。
o = odd() print "=======" print o.next() print o.next() print o.next() print o.next()
獲取yield的返回值(迭代器的返回值)可以使用.next()方法進行獲取,獲取完所有值就不再有值了,每獲取一個值迭代器指向的位置就會變化一次,直到取完所有值。
10、yield用法及示例(二):
#!/usr/bin/env python def gen(): value= 0 whileTrue: recv= yield value ifrecv == 'e': break value= "gen:%s" % recv g = gen() print g.send(None) #g.next() print g.send('aaaa') print g.send('bbbb') print g.send('cccc') print g.send('dddd')
在上面的程序中,我們主要是要介紹send()方法,是怎樣使用的,在函數定義中,我們看recv = yield value這一句,這一句是可以給yield傳進來值的,即yield是可以接受send過來的值。
由於yield的特殊性,它在返回一個值的時候就已經停下來了,所以第一次並沒有接受send的值,下面調用時的第一個g.send(None),相當於是g.next(),這就是讓yield在往下進行一次取值,跳過第一次返回值後就停止的狀態,接下來就是一次接受send的值,然後進行替換value值後返回,爲了測試可在前後加一些必要的測試語句查看有什麼不同,幫助理解。
下面是程序執行的結果:
0 gen:aaaa gen:bbbb gen:cccc gen:dddd
11、下面是改進以後的示例,加入了一些別的功能,可以幫助理解yield和send方法的使用:
#!/usr/bin/env python def sgen(): value= 0 whileTrue: rec= yield value ifrec == "quit": value= 'please input "q" to quit' elifrec == "hello": #print"hello,nihao" value= "hello,@you" else: value= "what are you say?" s = sgen() s.send(None) #跳出第一次停等狀態 while True: TEXT= raw_input("please input:") ifTEXT == "q": break prints.send(TEXT)
結果如下:
please input:ok what are you say? please input:hello hello,@you please input:quit please input "q" to quit please input:q