1. 程式人生 > >python中decorator的用法及原理(一)

python中decorator的用法及原理(一)

0、 概念

什麼叫裝飾器,其實也可以叫做包裝器。即對於一個既有的函式func(args),在呼叫它之前和之後,我們希望都做一些事情,把這個函式包裝起來。

python中的裝飾器分為兩類:函式裝飾器和類裝飾器。

這裡我們先討論函式裝飾器。

1. 不帶引數的decorator

(1) 基本用法:

def decorator1(func):
    def dec(*args):
        print 'pre action'
        result = func(*args)
        print 'post action'
        return result
    return dec

@decorator1
def test_f1(name):
    print name
    return None

test_f1('name1') #out: preaction/name1/post action
test_f1('name2') #out: preaction/name2/post action

(2) 這種現象的內部原理:

在python內部,當你做了這件事情:

@decorator1
def test_f1(name):

其實就是 test_f1 = decorator1(test_f1) #即test_f1作為引數傳遞給func。

此後的test_f1是裝飾器中的dec函式物件了,而不是原來的函式的名稱。當呼叫test_f1(‘name1’)的時候,其實呼叫的是dec(‘name1’)函式,而在dec函式內部,又呼叫了func,這樣就造成了裝飾器的效果。

這也解釋了func是被裝飾函式,*arg是被裝飾函式的引數—這種現象了。

2. 帶引數的decorator

(1) 基本用法:

def wap(name):
    def decorator1(func):
        def dec(*args):
            print name
            print 'pre action'
            result = func(*args)
            print 'post action'
            return result
        return dec
    return decorator1

@wap('f1')
def test_f1(name):
    print name
    return None

@wap('f2')
def test_f2(name):
    print name
    return None

test_f1('name1') #out: f1/pre action/name1/post action
test_f1('name2') #out: f2/pre action/name2/post action

帶引數的decorator,作用是通過傳遞引數可以定製不同的裝飾器。

(2) 內部原理

這裡和上面 不帶引數的decorator類似,

@wap('f1')
def test_f1(name):

內部邏輯為: test_f1 = wap(‘f1’)(test_f1)

這裡wap(‘f1’)返回是decorator1函式物件,這樣的話,wap(‘f1’)(test_f1)其實就是decorator1(test_f1),這樣就和上面的一樣了。只不過這裡傳遞了一個引數’f1’進入decorator內部,使得我們可以操作這個引數。

3. 函式decorator也可以修飾類成員函式

class FOO:
    @decorator1
    def fun(self):
        print self.name

注意此時fun的self會被傳遞到decorator1中。此時把self看做普通的函式入參。

4. 函式decorator的疊加

(1) 用法

def decorator1(func):
    def dec(*args):
        print 'd1 pre'
        result = func(*args)
        print 'd1 post'
        return result
    return dec

def decorator2(func):
    def dec(*args):
        print 'd2 pre'
        result = func(*args)
        print 'd2 post'
        return result
    return dec

@decorator1
@decorator2
def test(name):
    print name

test('test') #out: d1 pre/d2 pre/test/d1 post/d2 post

(2) 原理

@decorator1
@decorator2
def test(name):
    print name

和上面的類似,內部原理是:

test = decorator1(decorator2(test))

注意decorator1(decorator2(test)),不是說先執行decorator2(test),再執行decorator1。

而是先把decorator2(test)作為引數,最先執行decorator1,然後再執行decorator2.。

參考文章:http://thecodeship.com/patterns/guide-to-python-function-decorators/