網頁

2015年5月6日 星期三

python decorator

只是筆記一下

目的:
假設我們想要對某些function的回傳值做一個簡單的處理:
如果回傳值昰list且指有一個元素就直接取出(不放在list裡面)

def unpack_list_for_one_item(fn):                                                                            
    "unpack list for one item, if fn return ['5.1'] than return '5.1'"                                        
    def decorator(*args, **kwargs):                                                                          
        fnRet = fn(*args, **kwargs)                                                                          
        if not isinstance(fnRet, list):                                                                      
            raise TypeError("The decorator-unpack_list_for_one_item input fn{0}'s result shall be list type".format(fn))                                                                                                      
                                                                                                             
        return fnRet[0] if 1==len(fnRet) else fnRet                                                          
    return decorator                                                                                          

用法:
    @unpack_list_for_one_item
    def major(self):      
       ....

    @unpack_list_for_one_item
    def minor(self):      
       ....

    @unpack_list_for_one_item
    def patPath(self):      
       ....

解說:
1. decorator如果沒有額外參數input和output都是function
2. decorator即使沒有被呼叫也會被run過(第一層),且在main之前就被run過,如下:
#!/usr/bin/python3

from functools import wraps, partial
import logging

def logged(func):
        print ("%r %s" % (locals(), "0"))
        def innerDecorate(*args, **kwargs):
                print ("%r %s" % (locals(), "1"))
                return func
        return innerDecorate

@logged
def add(x, y):
        return x + y


注意, add並沒有被呼叫, 但結果如下:
{'func': <function add at 0x7f6798c77170>} 0

這表示 print ("%r %s" % (locals(), "0")) 有被執行
並且注意 print ("%r %s" % (locals(), "1")) 沒有被執行

但注意我們只是對func加上decorator

那如果是有參數的decorator會如何?
#!/usr/bin/python3



from functools import wraps, partial

import logging





def logged(level, name=None, message=None):

        print ("%r %s" % (locals(), "0"))

        def decorate(func):

                logname =  name if name else func.__module__

                log = logging.getLogger(logname)

                logmsg = message if message else func.__name__

                print ("%r %s" % (locals(), "1"))



                @wraps(func)

                def wrapper(*args, **kwargs):

                        print ("%r %s" % (locals(), "2"))

                        log.log(level, logmsg)

                        return func(*args, **kwargs)



                return wrapper

        return decorate



@logged(logging.DEBUG)

def add(x, y):

        return x + y


注意,一樣add並沒有被呼叫, 但結果如下:
{'message': None, 'level': 10, 'name': None} 0
{'message': None, 'logmsg': 'add', 'func': <function add at 0x7f8c9f649710>, 'logname': '__main__', 'name': None, 'log': <logging.Logger object at 0x7f8c9f66db10>, 'level': 10} 1

這表示 print ("%r %s" % (locals(), "0")) / print ("%r %s" % (locals(), "1"))  有被執行
並且注意 print ("%r %s" % (locals(), "2")) 沒有被執行