Decorator to print function call details - parameters names and effective values
我想创建一个函数,作为另一个函数的修饰器,它将打印该函数调用的详细信息-参数名称和有效值。我目前的实现就是这样。
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 | def describeFuncCall(func): '''Decorator to print function call details - parameters names and effective values''' def wrapper(*func_args, **func_kwargs): print 'func_code.co_varnames =', func.func_code.co_varnames print 'func_code.co_argcount =', func.func_code.co_argcount print 'func_args =', func_args print 'func_kwargs =', func_kwargs params = [] for argNo in range(func.func_code.co_argcount): argName = func.func_code.co_varnames[argNo] argValue = func_args[argNo] if argNo < len(func_args) else func.func_defaults[argNo - func.func_code.co_argcount] params.append((argName, argValue)) for argName, argValue in func_kwargs.items(): params.append((argName, argValue)) params = [ argName + ' = ' + repr(argValue) for argName, argValue in params] print(func.__name__ + ' ( ' + ', '.join(params) + ' )') return func(*func_args, **func_kwargs) return wrapper @describeFuncCall def test(a, b = 4, c = 'blah-blah', *args, **kwargs): pass test(1) #test(1, 3) #test(1, d = 5) test(1, 2, 3, 4, 5, d = 6, g = 12.9) |
有点效果,但有一些缺陷:
打电话
资讯科技印刷
预期结果是
我被困在这里了。你能帮我找到正确的解决办法吗?
对不起,有点乱。我修改了http://wiki.python.org/moin/pythondecoratorlibrary easy dump的函数参数中的一些代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 | def dump_args(func): "This decorator dumps out the arguments passed to a function before calling it" argnames = func.func_code.co_varnames[:func.func_code.co_argcount] fname = func.func_name def echo_func(*args,**kwargs): print fname,"(", ', '.join( '%s=%r' % entry for entry in zip(argnames,args[:len(argnames)])+[("args",list(args[len(argnames):]))]+[("kwargs",kwargs)]) +")" return echo_func @dump_args def test(a, b = 4, c = 'blah-blah', *args, **kwargs): pass |
试验(1,2,3,4,5,d=6,g=12.9)
输出:
测试(a=1,b=2,c=3,args=[4,5],kwargs='d':6,'g':12.9)
这是python 3.6的更新版本+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import inspect def dump_args(func): """Decorator to print function call details - parameters names and effective values. """ def wrapper(*args, **kwargs): func_args = inspect.signature(func).bind(*args, **kwargs).arguments func_args_str = ', '.join('{} = {!r}'.format(*item) for item in func_args.items()) print(f'{func.__module__}.{func.__qualname__} ( {func_args_str} )') return func(*args, **kwargs) return wrapper @dump_args def test(a, b=4, c='blah-blah', *args, **kwargs): pass test(1) test(1, 3) test(1, d=5) test(1, 2, 3, 4, 5, d=6, g=12.9) |
旧版本
使用默认值的工作版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | def dumpArgs(func): '''Decorator to print function call details - parameters names and effective values''' def wrapper(*func_args, **func_kwargs): arg_names = func.func_code.co_varnames[:func.func_code.co_argcount] args = func_args[:len(arg_names)] defaults = func.func_defaults or () args = args + defaults[len(defaults) - (func.func_code.co_argcount - len(args)):] params = zip(arg_names, args) args = func_args[len(arg_names):] if args: params.append(('args', args)) if func_kwargs: params.append(('kwargs', func_kwargs)) print func.func_name + ' (' + ', '.join('%s = %r' % p for p in params) + ' )' return func(*func_args, **func_kwargs) return wrapper @dumpArgs def test(a, b = 4, c = 'blah-blah', *args, **kwargs): pass test(1) test(1, 3) test(1, d = 5) test(1, 2, 3, 4, 5, d = 6, g = 12.9) |
结果:
1 2 3 4 | >>> test ( a = 1, b = 4, c = 'blah-blah' ) test ( a = 1, b = 3, c = 'blah-blah' ) test ( a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ) test ( a = 1, b = 2, c = 3, args = (4, 5), kwargs = {'d': 6, 'g': 12.9} ) |
下面是我在python 3中解决它的方法,基于aliteralmind的答案,如果我可以这么说的话,可以更清晰地放置(pep8)。清理工作的大部分灵感来自罗伯特·金(Robert King)目前接受的答案。
代码:
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 | import logging def log_function_entry_and_exit(decorated_function): """ Function decorator logging entry + exit and parameters of functions. Entry and exit as logging.info, parameters as logging.DEBUG. """ from functools import wraps @wraps(decorated_function) def wrapper(*dec_fn_args, **dec_fn_kwargs): # Log function entry func_name = decorated_function.__name__ log = logging.getLogger(func_name) log.info('Entering {}()...'.format(func_name)) # get function params (args and kwargs) arg_names = decorated_function.__code__.co_varnames params = dict( args=dict(zip(arg_names, dec_fn_args)), kwargs=dec_fn_kwargs) log.debug( "\t" + ', '.join([ '{}={}'.format(str(k), repr(v)) for k, v in params.items()])) # Execute wrapped (decorated) function: out = decorated_function(*dec_fn_args, **dec_fn_kwargs) log.info('Done running {}()!'.format(func_name)) return out return wrapper @log_function_entry_and_exit def func1(a, b, c): print(" \ty'elo2! ") @log_function_entry_and_exit def a(x, y, z): print(" \ty'elo! ") LOG_FORMAT = '[{}] !%(levelname)s! %(funcName)s: %(message)s'.format( _get_current_time_string(just_time_string=True)) logging.basicConfig(format=LOG_FORMAT, level=logging.DEBUG) a(x=1, y="b", z={'c': 2}) func1(2, b="y", c={'z': 4}) func1(2,"y", {'z': 4}) |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | In [6]: a(x=1, y="b", z={'c': 2}) ...: func1(2, b="y", c={'z': 4}) ...: func1(2,"y", {'z': 4}) ...: [2016.09.22 - 17:31:48] !INFO! wrapper: Entering a()... [2016.09.22 - 17:31:48] !DEBUG! wrapper: kwargs={'x': 1, 'z': {'c': 2}, 'y': 'b'}, args={} y'elo! [2016.09.22 - 17:31:48] !INFO! wrapper: Done running a()! [2016.09.22 - 17:31:48] !INFO! wrapper: Entering func1()... [2016.09.22 - 17:31:48] !DEBUG! wrapper: kwargs={'c': {'z': 4}, 'b': 'y'}, args={'a': 2} y'elo2! [2016.09.22 - 17:31:48] !INFO! wrapper: Done running func1()! [2016.09.22 - 17:31:48] !INFO! wrapper: Entering func1()... [2016.09.22 - 17:31:48] !DEBUG! wrapper: kwargs={}, args={'c': {'z': 4}, 'a': 2, 'b': 'y'} y'elo2! [2016.09.22 - 17:31:48] !INFO! wrapper: Done running func1()! |
注意:输出中的
使用实例:
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 | In [1]: from meh import execute_os_command In [2]: from meh import LOG_FORMAT In [3]: import logging In [4]: logging.basicConfig(format=LOG_FORMAT, level=logging.INFO) ...: ...: logging.info("Entered script... ") ...: ...: result = execute_os_command(cmd=["echo","trololol"]) ...: print(" {} ".format(result)) ...: execute_os_command(cmd=["echo","trololol"], dry_run=True) ...: ...: logging.info("Exiting script... ") ...: [2016.09.22 - 17:42:19] !INFO! <module>: Entered script... [2016.09.22 - 17:42:19] !INFO! wrapper: Entering execute_os_command()... [2016.09.22 - 17:42:19] !INFO! execute_os_command: Executing: [2016.09.22 - 17:42:19] !INFO! execute_os_command: echo trololol [2016.09.22 - 17:42:19] !INFO! execute_os_command: Waiting for above command to finish execution... [2016.09.22 - 17:42:19] !INFO! wrapper: Done running execute_os_command()! {'stderr': '', 'stdout': 'trololol ', 'command': ['echo', 'trololol'], 'returncode': 0, 'stdin': None, 'timedout': False} [2016.09.22 - 17:42:19] !INFO! wrapper: Entering execute_os_command()... [2016.09.22 - 17:42:19] !INFO! execute_os_command: Would have executed: [2016.09.22 - 17:42:19] !INFO! execute_os_command: echo trololol [2016.09.22 - 17:42:19] !INFO! execute_os_command: Exiting execute_os_command()... [2016.09.22 - 17:42:19] !INFO! wrapper: Done running execute_os_command()! [2016.09.22 - 17:42:19] !INFO! <module>: Exiting script... In [5]: |
当我得到被称为"时间和能量"的神奇资源时,我很想和
@Warvariuc的答案,升级到python 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | def dumpArgs(func): '''Decorator to print function call details - parameters names and effective values''' def wrapper(*func_args, **func_kwargs): arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] args = func_args[:len(arg_names)] defaults = func.__defaults__ or () args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):] params = list(zip(arg_names, args)) args = func_args[len(arg_names):] if args: params.append(('args', args)) if func_kwargs: params.append(('kwargs', func_kwargs)) print(func.__name__ + ' (' + ', '.join('%s = %r' % p for p in params) + ' )') return func(*func_args, **func_kwargs) return wrapper @dumpArgs def test(a, b = 4, c = 'blah-blah', *args, **kwargs): pass test(1) test(1, 3) test(1, d = 5) test(1, 2, 3, 4, 5, d = 6, g = 12.9) |
这是一个有点老的帖子,但想增加我的一点。Warvariuc给出的解决方案并不适用于所有情况。如果一个方法具有默认值,并且我们在调用时发送命名参数,那么它不会给出正确的输出。例如,我们得到两个b值。
1 2 | test(1, b = 5) test (a = 1, b = 4, c = 'blah-blah', kwargs = {'b': 5} ) |
添加我修改的代码。
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 | def print_args(func): """ Function to print all args of decorated function """ def wrapper(*func_args, **func_kwargs): arg_names = func.func_code.co_varnames[:func.func_code.co_argcount] args = func_args[:len(arg_names)] defaults = func.func_defaults or () args = args + defaults[len(defaults) - (func.func_code.co_argcount - len(args)):] params = zip(arg_names, args) new_arg_list = [list(i) for i in params] for key in func_kwargs: for param in new_arg_list: if key == param[0]: param[1] = func_kwargs[key] new_arg_list = [tuple(i) for i in new_arg_list] print func.func_name + ' (' + ', '.join('%s = %r' % p for p in new_arg_list) + ' )' return func(*func_args, **func_kwargs) return wrapper @print_args def test_params(a=7,b=5): pass test_params(a=3) |
产量
1 | test_params (a = 3, b = 5 ) |