在使用具有可调用对象的装饰器时返回用户定义的函数名称

时间:2011-05-28 20:53:17

标签: python decorator callable function-object

考虑以下代码片段。

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

3 个答案:

答案 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):
    ...

无需更改;你的包装对象现在是一个函数,它应该是一个类,但我从你的问题推断(事实上它是一个可调用的类)它并不重要(如果它确实,你可以修改装饰器到使返回的包装对象“更漂亮”。)