欢迎各位兄弟 发布技术文章
这里的技术是共享的
下面这个示例演示了一个logger的decorator,这个decorator输出了函数名,参数,返回值,和运行时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from functools import wraps def logger(fn): @wraps (fn) def wrapper( * args, * * kwargs): ts = time.time() result = fn( * args, * * kwargs) te = time.time() print "function = {0}" . format (fn.__name__) print " arguments = {0} {1}" . format (args, kwargs) print " return = {0}" . format (result) print " time = %.6f sec" % (te - ts) return result return wrapper @logger def multipy(x, y): return x * y @logger def sum_num(n): s = 0 for i in xrange (n + 1 ): s + = i return s print multipy( 2 , 10 ) print sum_num( 100 ) print sum_num( 10000000 ) |
上面那个打日志还是有点粗糙,让我们看一个更好一点的(带log level参数的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import inspect def get_line_number(): return inspect.currentframe().f_back.f_back.f_lineno def logger(loglevel): def log_decorator(fn): @wraps (fn) def wrapper( * args, * * kwargs): ts = time.time() result = fn( * args, * * kwargs) te = time.time() print "function = " + fn.__name__, print " arguments = {0} {1}" . format (args, kwargs) print " return = {0}" . format (result) print " time = %.6f sec" % (te - ts) if (loglevel = = 'debug' ): print " called_from_line : " + str (get_line_number()) return result return wrapper return log_decorator |
但是,上面这个带log level参数的有两具不好的地方,
1) loglevel不是debug的时候,还是要计算函数调用的时间。
2) 不同level的要写在一起,不易读。
我们再接着改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import inspect def advance_logger(loglevel): def get_line_number(): return inspect.currentframe().f_back.f_back.f_lineno def _basic_log(fn, result, * args, * * kwargs): print "function = " + fn.__name__, print " arguments = {0} {1}" . format (args, kwargs) print " return = {0}" . format (result) def info_log_decorator(fn): @wraps (fn) def wrapper( * args, * * kwargs): result = fn( * args, * * kwargs) _basic_log(fn, result, args, kwargs) return wrapper def debug_log_decorator(fn): @wraps (fn) def wrapper( * args, * * kwargs): ts = time.time() result = fn( * args, * * kwargs) te = time.time() _basic_log(fn, result, args, kwargs) print " time = %.6f sec" % (te - ts) print " called_from_line : " + str (get_line_number()) return wrapper if loglevel is "debug" : return debug_log_decorator else : return info_log_decorator |
你可以看到两点,
1)我们分了两个log level,一个是info的,一个是debug的,然后我们在外尾根据不同的参数返回不同的decorator。
2)我们把info和debug中的相同的代码抽到了一个叫_basic_log的函数里,DRY原则。
下面这个decorator是我在工作中用到的代码,我简化了一下,把DB连接池的代码去掉了,这样能简单点,方便阅读。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import umysql from functools import wraps class Configuraion: def __init__( self , env): if env = = "Prod" : self .host = "coolshell.cn" self .port = 3306 self .db = "coolshell" self .user = "coolshell" self .passwd = "fuckgfw" elif env = = "Test" : self .host = 'localhost' self .port = 3300 self .user = 'coolshell' self .db = 'coolshell' self .passwd = 'fuckgfw' def mysql(sql): _conf = Configuraion(env = "Prod" ) def on_sql_error(err): print err sys.exit( - 1 ) def handle_sql_result(rs): if rs.rows > 0 : fieldnames = [f[ 0 ] for f in rs.fields] return [ dict ( zip (fieldnames, r)) for r in rs.rows] else : return [] def decorator(fn): @wraps (fn) def wrapper( * args, * * kwargs): mysqlconn = umysql.Connection() mysqlconn.settimeout( 5 ) mysqlconn.connect(_conf.host, _conf.port, _conf.user, \ _conf.passwd, _conf.db, True , 'utf8' ) try : rs = mysqlconn.query(sql, {}) except umysql.Error as e: on_sql_error(e) data = handle_sql_result(rs) kwargs[ "data" ] = data result = fn( * args, * * kwargs) mysqlconn.close() return result return wrapper return decorator @mysql (sql = "select * from coolshell" ) def get_coolshell(data): ... ... ... .. |
下面量个非常简单的异步执行的decorator,注意,异步处理并不简单,下面只是一个示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from threading import Thread from functools import wraps def async(func): @wraps (func) def async_func( * args, * * kwargs): func_hl = Thread(target = func, args = args, kwargs = kwargs) func_hl.start() return func_hl return async_func if __name__ = = '__main__' : from time import sleep @async def print_somedata(): print 'starting print_somedata' sleep( 2 ) print 'print_somedata: 2 sec passed' sleep( 2 ) print 'print_somedata: 2 sec passed' sleep( 2 ) print 'finished print_somedata' def main(): print_somedata() print 'back in main' print_somedata() print 'back in main' main() |
关于更多的示例,你可以参看: Python Decorator Library
关于Python Decroator的各种提案,可以参看:Python Decorator Proposals
(全文完)