1. 程式人生 > >No.9 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. 文章不妥之處還望指正