[Python 實戰] - No.9 Python閉包和裝飾器

一、閉包:

閉包就是內層函數引用了外部函數的變量,然後返回內層函數的情況。閉包的特點就是我們返回的函數,引用了外部函數的局部變量,如果我們希望按照我們所想,來正確的使用這個閉包的話,那就要確定我們引用的局部變量在函數返回以後不能更改。

一個最簡單的閉包:

def outer(arg):
    def inner():
        return 'Using args:' + arg
    return inner

f = outer('hello')
print f()
例如上面的例子,我們定義了一個outer()函數,同時在裏面定義了一個inner()函數。inner()函數使用了outer()中的參數arg,然後我們返回inner函數。所以總的來說,outer函數的返回值是內部定義的inner,同時inner使用了outer()中的參數。

那麼什麼叫外部的參數不能改變呢?

比如,我們定義一個函數getFunc(),這個函數返回了三個函數,分別定義1+1,2+2,3+3

def getFaunc():
    L = []
    for i in range(1,4):
        def f():
            return i+i
        L.append(f)
    return L
f1,f2,f3 = getFaunc()
print f1(),f2(),f3()
結果發現,最後的結果均是3+3。因爲在我們添加完第三個函數的時候,i=3,內部函數使用到的外部參數改變了,所以我們的閉包沒有正確運行

更改爲如下所示:

def getFaunc():
    L = []
    for i in range(1,4):
        def f(i):
            def g():
                return i+i
            return g
        tmp = f(i)
        L.append(tmp)
    return L
f1,f2,f3 = getFaunc()
print f1(),f2(),f3()
區別就是,在第二段代碼中,我們執行了f(i)函數,這就使得相對的g中的變量i固定爲當前f執行時的i,從而不會導致外部的變量改變這種情況


二、Python裝飾器

Python裝飾器,顧名思義就是給函數起到裝飾功能,這就意味着:首先是僅是裝飾而不會更改函數的任何代碼,第二個就是就是增加原有函數的功能。

舉個例子,我們有一個函數是打印當天的日期:

import time


def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

print getDate()
如果我們希望在打印日期的時候,在服務器上打印當前的時間,而又不希望更改代碼。這時候就可以使用裝飾器

import time


def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

def getDateAndTime(f):  # 裝飾器函數,將原函數作爲參數傳入裝飾器函數
    def newGetDate():   #在內部聲明一個新函數
        print '[SERVER] Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time())) #增加的新功能
        return f() # 返回原函數執行
    return newGetDate #返回新函數

getDate = getDateAndTime(getDate) #屏蔽掉原有函數

print getDate()
裝飾器看上去很複雜,自底向上拆成以下步驟很好理解:

1. 首先,要屏蔽掉原有函數,將原有函數傳入裝飾器函數,獲得新函數,所以我們需要構造裝飾器函數

2. 在定義的裝飾器函數內部,我們需要定義一個新的函數,擴展功能,並作爲裝飾器函數的返回值


3. 在新函數的內部,擴展新的函數。同時將原函數執行,並作爲 返回值返回。

在python中使用這種方法來實現切面。同時,python提供了@這樣的語法糖,讓我們可很好的使用裝飾器,而不用每次都是使用getDate = getDateAndTime(getDate),來屏蔽原有函數

import time

def getDateAndTime(f):
    def newGetDate():
        print '[SERVER] Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time()))
        return f()
    return newGetDate

@getDateAndTime
def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

print getDate()
與之前相比,我們只需要將@+裝飾器函數標註在原有函數上,就實現屏蔽原函數的作用。除此之外,python裝飾器也可以帶上參數

import time

def log(arg):
    def getDateAndTime(f):
        def newGetDate():
            print '['+arg+']' +'Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time()))
            return f()
        return newGetDate
    return getDateAndTime

@log('SERVER')
def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

print getDate()
在原有的基礎上,多套了一層函數來接收參數。

上面代碼可以解釋爲:

getDateAndTime = log('SERVER')

getDate = getDateAndTime(getDate)


P.S. 文章不妥之處還望指正


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