ReadEasy

05 5月, 2013

Learning Python @Decorator

最近學習到AOP這個專有名詞  了解到原來Python的decorator算是AOP的一種實現

之前對於decorator的使用懵懵懂懂  剛剛花了不少時間Google幾個範例跟文章

對於decorator的概念有更明確的了解  特別紀錄





以下是對岸的一個範例
def logger(name): 
    def loggerWrapper(fun): 
        def logging(self): 
            print "User is %s." % name 
            print "Start Logging" 
            result = fun(self) 
            print "End Logging." 
            return result 
        return logging 
    return loggerWrapper 
 
def debugger(name): 
    def debuggerWrapper(fun): 
        def debugging(self): 
            print "Debug %s" % name 
            print "Start Debug" 
            result = fun(self) 
            print "End Debug" 
            return result 
        return debugging 
    return debuggerWrapper 
 
class MyClass(): 
    @logger("Leven") 
    @debugger("test") 
    def Test(self): 
        print("function MyClass::Test called.") 
        return "I am Reuslt." 
         
 
if __name__ == "__main__": 
    mc = MyClass() 
    print "Result:%s" % mc.Test()


老實說這個範例有點亂  但仔細分析大致上清楚的呈現decorator的用法

還是搞不太懂 ? 了解這個原則我想應該都能稍微明白decorator的用法


def fn():
    print 'func called.'

p = fn
p()
首先要知道 p跟fn做一樣的事情, 結果是fn()間接被呼叫

再來這個例子對於函式的呼叫會更明確

def fn():
    print 'func called.'
    return 999

p = fn()
print p # > func called. & 999


接下來看看decorator的用法 會發現一些有趣的特性
def dec(fn):
    print fn.__name__
    return 123

#hello = dec( hello() )    
@dec
def hello():
    print 1

print hello

結果是
hello
123

很明顯decorator將hello()作為arg放進dec(fn) ,其中fn就是hello()

再來decorator會處理hello如同註解變成以下的樣子
hello = dec( hello() )


很有趣, 你會發現直接呼叫hello()會噴錯誤
Traceback (most recent call last):
  File "decorator.py", line 48, in
    hello()
TypeError: 'int' object is not callable
python告訴我們 「int」是不能被呼叫的, 所以這似乎代表著hello()變成一個變數了!

呼應前面 hello = dec( hello() ) , 我們在decorator直接回傳 123


而且hello()內的print 1並沒有被執行
若換成下面這樣, hello()內的statement就會被呼叫
def dec(fn):
    print fn.__name__
    fn()
    return 123

#hello = dec( hello() )    
@dec
def hello():
    print 1

print hello


output:
hello
1
123
以及直接return回函式, 這是比較常見的做法

因為直接return原函式, 即 hello = dec( hello() ), hello = hello()


def dec(fn):
    print fn.__name__
    return fn

#hello = dec( hello() )
@dec
def hello():
    print 1

print hello

hello
 (hello()的位置)

請注意是print hello, 而非print hello()

所以會將hello()的位置回傳, 這也意味著在return fn的地方python對hello賦予原本的hello()位置
不同於return fn()會直接執行函式, 必須以hello()呼叫才會執行print 1.

以上, 簡單介紹關於decorator

關於decorator的應用感覺還蠻彈性的

這個範例

from django.http import HttpResponsePermanentRedirect
from google.appengine.api import users

def loginrequired(func):
    def redirect_to_login(request):
        return HttpResponsePermanentRedirect(users.create_login_url(request.get_full_path()))

    user = users.get_current_user()
    if user:
        return func
    else:
        return redirect_to_login

@loginrequired
def i_am_protected(request):
    pass # clearly, this is a top secret view that needs protecting.

將authorize 寫成decorator就可以很方便的重用

就省了另外寫判斷式的麻煩  原來這就是AOP的強大啊!!!


Ref:
    快快樂樂學 Python decorator - http://blog.dorm7.com/
    decorator的使用 - http://blog.donews.com/limodou
    Python裝飾器學習(九步入門)- http://www.cnblogs.com/rhcad/

以下是我自己嘗試的用法, 還蠻有趣的


def dec(fn):
    def pro(arg):
        print arg # arg = hell() , fn called.
        return fn()

    print fn.__name__
    return pro(fn)
    

#hello = hello( dec( pro( hello() ) ) )    
@dec
def hello(x):
    print 1
    return 999


print hello
順序其實照註解寫的可能有點亂, 其中被呼叫的流程是dec( hello() ) >>> dec( fn )
print hello().__name__ >>> print fn.__name__
然後將控制權交給dec()底下的pro()並且是以pro(fn)呼叫 >>> pro( hello() )

hello()的位置被當成pro(arg), pro的參數被傳入hello()的位置

所以arg會噴出hello()的位置訊息, 然後return fn()將控制權交給原本的hello()並且執行

所以print 1會被執行, return 999

注意因為decorator的影響, hello已經變成
hello = 999
print hello >>> 999
,而非再次執行hello函式


(待補)
def dec(fn):
    def pro(arg):
        print arg
        return 123

    print fn.__name__
    return fn
    

#hello = dec( hello() )    
@dec
def hello(x):
    print 1

#hello = fn
hello(999)

沒有留言:

張貼留言