python學習筆記之函數總結--高階函數以及裝飾器

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)

在上面的函數體中,我們對內部函數進行了改進,其中startend分別是獲取當前系統時間,中間是調用了一個函數f,所以endstart的差值就是函數f執行的時間cost,爲了顯示的是秒,我們對cost做了一個轉化爲秒數的改動,即調用total_seconds方法。

下面是程序執行後的結果:

ext_func
3.004226

首先將func函數結果傳遞給變量f的時候,打印了一句ext_func語句,這時函數f其實就是inn_func函數,因爲func函數的返回值就是inn_func函數;調用f函數,這時我們要傳遞一個參數進去,而這個參數還是另一個函數,我們傳遞的是自定義的f2函數,它sleep3秒,所以兩個查找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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章