我一直在学习Python装饰器,并练习使用它们,我正在编写一个日志装饰器,它记录函数何时调用、*args和**kwargs传递给函数的时间,以及函数的__repr__,以便以后可以重新评估它。
在Python中,对象的__repr__()方法返回一个字符串,有时可以通过将该字符串传递给eval()来重新创建该对象。我正在尝试为函数实现一种形式的__repr__。
由于我使用的是包装任何函数的装饰符,所以我不知道函数的参数是什么,所以我需要使用*args和**kwargs来构造__repr__,这是一个字符串,看起来如下所示:
"function_name(positional_arg1, positional_arg2, keyword_arg1='some_value', keyword_arg2='other_value')"为了创建这个字符串表示,我需要从`kwargs**; that is, convert a dictionary in the form of{'kwarg1':val1(‘kwarg2 2’:val2}to an argument list like:kwarg1=val1,kwarg2=val2` )重构一个参数列表。
(注意:将*args解压缩到参数列表不是问题,因为元组形式的args已经是一种可以接受的格式,可以在去掉元组中的括号后作为位置参数传递给函数。也就是说,args元组:('arg1', 'arg2')只是变成了'arg1', 'arg2'。因此,这个问题的重点是将kwargs字典转换回参数列表。)
下面是我迄今为止所创造的。它很好用,但不太优雅。是否有一种与解压缩kwargs**?**相反的更简单的方法来执行
编辑:我删除了补充代码(例如,设置装潢器),以专注于手头的问题:反包装操作。
def print_args(*args, **kwargs):
# Generate the kwargs string representation
kwargs_repr = ''
# If no kwargs are passed, kwargs holds an empty dictionary (i.e., dict())
if kwargs != dict():
num_kwargs = len(kwargs)
# Convert to format required for an argument list (i.e., key1=val1 rather than {key1: val1})
for n, (kw, val) in enumerate(kwargs.items()):
kwargs_repr += str(kw) + '='
# If the value is a string, it needs extra quotes so it stays a string after being passed to eval().
if type(val) == str:
kwargs_repr += '"' + val + '"'
else:
kwargs_repr += str(val)
# Add commas to separate arguments, up until the last argument
if n < num_kwargs - 1:
kwargs_repr += ', '
repr = (
"print_args("
# str(args)[1:-1] removes the parentheses around the tuple
f"{str(args)[1:-1] if args != tuple() else ''}"
f"{', ' if args != tuple() and kwargs != dict() else ''}"
f"{kwargs_repr})"
)
print(repr)
return repr
returned_repr = print_args('pos_arg1', 'pos_arg2', start=0, stop=10, step=1) 输出:
print_args('pos_arg1', 'pos_arg2', start=0, stop=10, step=1)(归因:我的技术的一些灵感来自于这个堆栈溢出答案:https://stackoverflow.com/a/10717810/17005348)。
注释到目前为止我尝试过的
args和kwargs,比如:# If I know the name of the function
# and have stored the args and kwargs passed to the function:
any_func(*args, **kwargs)
# Or, more generally:
kwargs = str(kwargs)[1:-1].replace(': ', '=').replace("'", '')
eval(
# func_name holds the __name__ attribute of the function
func_name
+ '('
+ str(args)[1:-1]
+ ','
+ str(kwargs)
+ ')'
)..。但是,如果可能的话,我希望使用repr表单,以模仿用于创建对象的语法(即eval(repr(obj))),并避免泛型版本中使用的混乱的字符串连接。
eval()不能将其识别为关键字参数列表。例如:print(tuple(str(k) + '=' + str(v) for k, v in kwargs.items()))输出('key1=val1', 'key2=val2')而不是('key1=val1, key2=val2')。
repr()函数,如:repr(func(*args, **kwargs))。这没有起作用,因为首先计算的是func().,因此repr()只返回由repr()返回的值的字符串表示形式
发布于 2022-07-07 03:41:40
这里有一些严重的警告,因为并非所有传递给任何类型关键字参数的内容都必须有良好的表示,而且表示可能不像您预期的那样工作(在计算时重构原始文本的副本)。
话虽如此,如下所述:
def print_args(*args, **kwargs):
print(', '.join(map(repr, args)),
', '.join(f'{k}={repr(v)}' for k, v in kwargs.items()))
print_args(1, 'test', [1, 2, 3], a='word', b={'key': 1, 'another': 2})输出:
1, 'test', [1, 2, 3] a='word', b={'key': 1, 'another': 2}注意:我没有费心在这两个部分之间打印逗号,我想这是很明显的事情。但万一它惹恼你:
def print_args(*args, **kwargs):
print(', '.join(list(map(repr, args)) +
[f'{k}={repr(v)}' for k, v in kwargs.items()]))输出:
1, 'test', [1, 2, 3], a='word', b={'key': 1, 'another': 2}https://stackoverflow.com/questions/72891810
复制相似问题