这是我们面对装饰时遇到的第一个例子。但是我无法意识到我到底想要什么。
一个名为LOG的简单装饰器。它应该像这样工作:
@LOG
def f(a, b=2, *c, **d):
pass
结果应该是这样的:
f(1, pippo=4, paperino='luca')
===== Enter f =====
a = 1
b = 2
pippo = 4
paperino = luca
===== Exit f =====
将作为参数传递给函数的每个参数显示为其值。
我发现这个问题比我想象的要困难,主要是因为你可以通过许多不同的方式将参数传递给函数(想想带有* c的元组或带有** d的字典)。
我尝试了一个解决方案,但我不确定它是否正确。它有点像这样:
def LOG(fn):
import inspect
varList, _, _, default = inspect.getargspec(fn)
d = {}
if default is not None:
d = dict((varList[-len(default):][i], v) for i, v in enumerate(default))
def f(*argt, **argd):
print ('Enter %s' % fn).center(100, '=')
d.update(dict((varList[i], v) for i, v in enumerate(argt)))
d.update(argd)
for c in d.iteritems():
print '%s = %s' % c
ret = fn(*argt, **argd)
print 'return: %s' % ret
print ('Exit %s' % fn).center(100, '=')
return ret
return f
我认为这并不像我预期的那么容易,但奇怪的是我没有在Google上找到我想要的东西。
你能告诉我我的解决方案是否合适?或者你能为我提出的问题提出更好的解决方案吗?
谢谢大家。
答案 0 :(得分:5)
我唯一注意到你使用两次的dict((varList[i], v) for i, v in enumerate(argt))
构造实际上是dict(zip(varList,argt))
。
除此之外我只有元批评:以上都不属于日志文件。
而不是将日志传递给
答案 1 :(得分:1)
你的功能正常。你似乎迷失了位置与变量和关键字参数。
让我解释一下:在您的情况下,位置参数a
和b
是强制性的(并且可能具有默认值)。其他参数是可选的。如果你想要一个参数必须或有一个默认值,把它放在* args和** kwargs之前。但请记住,你不能提供两次论证:
def x(a = 1, b = 2, *args, **kwargs):
print a, b, args, kwargs
>>> x(3, 4, 5, b=6)
TypeError: x() got multiple values for keyword argument 'b'
还有另一种方法,但不是那么可读,有参数的默认值,没有位置参数:
def x(*args, **kwargs):
kwargs.updae({'a': 1, 'b': 2})
分析参数的函数没问题,但我不明白为什么要将varargs
和keywords
写入_
。它透明地传递参数:
def x(a = 1, b = 2, *args, **kwargs):
print a, b, args, kwargs
def y(*args, **kwargs):
x(*args, **kwargs)
>>> y(3, 4, 5, 6)
3 4 (5, 6) {}
>>> y(3, 4, 5, b=6)
TypeError: x() got multiple values for keyword argument 'b'
答案 2 :(得分:0)
如果你考虑到一般函数理论上可以返回一个iterable,我发现你的好解决方案可以稍微改进一下,在这种情况下会抛出错误。
这是一个解决方案:
将
print 'return: %s' % ret
包裹在if语句中:
if hasattr(ret, "__iter__"): print 'returned iterable' else: print 'return: %s' % ret
这样你就不会花费大量时间打印大型迭代,但当然可以根据需要进行修改。 (另外一个字符串没有__iter__
属性,这很方便)