考虑以下代码片段。
def print_timing(func):
import time
def wrapper(*args, **kwargs):
t1 = time.time()
res = func(*args, **kwargs)
t2 = time.time()
print '%s took %0.3f s ~ %0.0f min and %0.1f sec' % (func.func_name, t2-t1, int(t2 - t1)/60, (t2-t1) % 60 )
return res
return wrapper
@print_timing |
def foo(): |
return 'foo'
class name(object):
def __init__(self, name):
self.name = name
@print_timing
def __call__(self):
return self.name
bar = name("bar")
print bar()
返回:
__call__ took 0.000 s ~ 0 min and 0.0 sec
bar
对象bar
的行为类似于名为bar
的函数,但在与装饰器__call__
一起使用时会公开print_timing
的内部实现细节。有没有办法更改name
对象(可能通过将合适的参数传递给__init__
函数),所以它返回
bar took 0.000 s ~ 0 min and 0.0 sec
?我想要一个让print_timing
装饰器继续使用普通函数的解决方案。运行
print foo()
给出了
foo took 0.000 s ~ 0 min and 0.0 sec
foo
答案 0 :(得分:1)
没有。装饰器在构建类时发生,并且在构建实例时发生__init__()
调用。您需要让装饰器将函数转换为描述符并让该描述符从实例中获取名称。
答案 1 :(得分:1)
只要你在方法上使用装饰器,它们就会被传递self
作为第一个参数:
def print_timing(func):
import time
def wrapper(*args, **kwargs):
t1 = time.time()
res = func(*args, **kwargs)
t2 = time.time()
funcname = func.__name__
# Special case; a "name" instance has a "name" attribute we want to use instead.
if len(args) >= 1 and isinstance(args[0], name):
funcname = args[0].name
print '%s took %0.3f s ~ %0.0f min and %0.1f sec' % (funcname, t2-t1, int(t2 - t1)/60, (t2-t1) % 60 )
return res
return wrapper
已更新:默认情况下,包装器现在使用func.__name__
,但如果您在name
类上使用此功能(如原始问题中所示),它将使用而是实例的name
属性。
我使用isinstance
测试来确定是否存在name
属性,但您可以使用duck-typing(if hasattr(args[0], 'name')
); name
变量是如此通用,但是当你在任意类方法上使用时,你很可能会得到意想不到的结果。
答案 2 :(得分:1)
使用@print_timing
作为类装饰器:
@print_timing
class name(object):
...
无需更改;你的包装对象现在是一个函数,它应该是一个类,但我从你的问题推断(事实上它是一个可调用的类)它并不重要(如果它确实,你可以修改装饰器到使返回的包装对象“更漂亮”。)