我想创建一个函数,作为另一个函数的装饰器将打印该函数调用细节 - 参数名称和有效值。我目前的实施是这样的。
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)
有点工作,但有一些错误:
致电
test(1, 2, 3, 4, 5, d = 6, g = 12.9)
打印
test ( a = 1, b = 2, c = 3, d = 6, g = 12.9 )
。
预期结果是
test ( a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} )
我被困在这里。你能帮我找到合适的解决方案吗?
答案 0 :(得分:12)
抱歉它有点乱。我修改了http://wiki.python.org/moin/PythonDecoratorLibrary#Easy_Dump_of_Function_Arguments
中的一些代码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})
答案 1 :(得分:10)
这是Python 3.6 +
的更新版本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)
旧版
使用默认值的工作版本:
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)
结果:
>>> 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} )
答案 2 :(得分:6)
以下是我在Python 3中基于aliteralmind's回答解决问题的方法,如果可以这样说,请更加干净(PEP8)。 清理的大部分灵感来自({当前)accepted answer Robert King。
代码:
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("\n\ty'elo2!\n")
@log_function_entry_and_exit
def a(x, y, z):
print("\n\ty'elo!\n")
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})
输出:
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()!
注意:输出中的wrapper
字符串表示调用logging.X()
消息的函数名称。
使用示例:
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...\n")
...:
...: result = execute_os_command(cmd=["echo", "trololol"])
...: print("\n{}\n".format(result))
...: execute_os_command(cmd=["echo", "trololol"], dry_run=True)
...:
...: logging.info("Exiting script...\n")
...:
[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\n', '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]:
当我获得被称为“时间和能量”的神奇资源时,我有兴趣使用LOG_FORMAT
,并弄清楚如何用say filename和linenumber替换wrapper
子字符串函数调用=)
答案 3 :(得分:4)
@ warvariuc的回答,升级到Python 3:
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)
答案 4 :(得分:0)
帖子有点旧,但想补充一下。 warvariuc给出的解决方案并非在所有情况下都有效。如果某个方法具有默认值,并且在调用时我们发送了命名参数,则它不会提供正确的输出。例如我们得到b的两个值。
test(1, b = 5)
test (a = 1, b = 4, c = 'blah-blah', kwargs = {'b': 5} )
添加修改后的代码。
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)
输出
test_params (a = 3, b = 5 )