66

是否可以有条件地装饰函数。例如,我想foo()用定时器函数(timeit)装饰函数,只有doing_performance_analysis是True(见下面的伪代码)。

if doing_performance_analysis:
  @timeit
  def foo():
    """
    do something, timeit function will return the time it takes
    """
    time.sleep(2)
else:
  def foo():
    time.sleep(2)  
4

6 回答 6

81

装饰器只是返回替换的可调用对象,可以选择相同的函数、包装器或完全不同的东西。因此,您可以创建一个条件装饰器:

def conditional_decorator(dec, condition):
    def decorator(func):
        if not condition:
            # Return the function unchanged, not decorated.
            return func
        return dec(func)
    return decorator

现在你可以像这样使用它:

@conditional_decorator(timeit, doing_performance_analysis)
def foo():
    time.sleep(2)  

装饰器也可以是一个类:

class conditional_decorator(object):
    def __init__(self, dec, condition):
        self.decorator = dec
        self.condition = condition

    def __call__(self, func):
        if not self.condition:
            # Return the function unchanged, not decorated.
            return func
        return self.decorator(func)

这里的__call__方法与第一个示例中返回的嵌套函数的作用相同,这里decorator()的封闭deccondition参数作为参数存储在实例上,直到应用装饰器。

于 2012-05-23T17:24:08.507 回答
18

装饰器只是一个应用于另一个函数的函数。您可以手动应用它:

def foo():
   # whatever
   time.sleep(2)

if doing_performance_analysis:
    foo = timeit(foo)
于 2012-05-23T17:23:07.853 回答
9

怎么样:

def foo():
   ...

if doing_performance_analysis:
   foo = timeit(foo)

我想你甚至可以将它包装到一个带有布尔标志和另一个装饰器的装饰器中,并且只有在标志设置为时才应用后者True

def cond_decorator(flag, dec):
   def decorate(fn):
      return dec(fn) if flag else fn
   return decorate

@cond_decorator(doing_performance_analysis, timeit)
def foo():
   ...
于 2012-05-23T17:22:10.050 回答
3
use_decorator = False

class myDecorator(object):
    def __init__(self, f):
            self.f = f

    def __call__(self):
            print "Decorated running..."
            print "Entering", self.f.__name__
            self.f()
            print "Exited", self.f.__name__


def null(a):
    return a


if use_decorator == False :
    myDecorator = null


@myDecorator
def CoreFunction():
    print "Core Function running"

CoreFunction()
于 2016-09-17T00:10:41.150 回答
1

如果您想在每次调用该函数时都进行检查,那么 Blckknght 的答案非常好,但是如果您有一个可以读取一次并且永不更改的设置,您可能不想在每次调用装饰函数时都检查该设置。在我们工作中的一些高性能守护进程中,我编写了一个装饰器,它在第一次加载 python 文件时检查设置文件并决定是否应该包装它。

这是一个示例

def timed(f):
    def wrapper(*args, **kwargs):
        start = datetime.datetime.utcnow()
        return_value = f(*args, **kwargs)
        end = datetime.datetime.utcnow()
        duration = end - start

        log_function_call(module=f.__module__, function=f.__name__, start=__start__, end=__end__, duration=duration.total_seconds())
    if config.get('RUN_TIMED_FUNCTIONS'):
        return wrapper
    return f

假设 log_function_call 记录您对数据库、日志文件或其他任何内容的调用,并且 config.get('RUN_TIMED_FUNCTIONS') 检查您的全局配置,那么将 @timed 装饰器添加到函数将在加载时检查一次以查看您是否正在计时此服务器、环境等,如果没有,则不会更改功能在生产环境或您关心性能的其他环境中的执行。

于 2014-01-16T22:15:11.937 回答
0

这对我有用:

def timeit(method):
    def timed(*args, **kw):
        if 'usetimer' not in kw:
            return method(*args, **kw)
        elif ('usetimer' in kw and kw.get('usetimer') is None):
            return method(*args, **kw)
        else:
            import time
            ts = time.time()
            result = method(*args, **kw)
            te = time.time()
            if 'log_time' in kw:
                name = kw.get('log_name', method.__name__.upper())
                kw['log_time'][name] = int((te - ts) * 1000)
            else:
                print '%r took %2.2f ms' % \
                      (method.__name__, (te - ts) * 1000)
            return result
    return timed

def some_func(arg1, **kwargs):
    #do something here

some_func(param1, **{'usetimer': args.usetimer})
于 2018-11-21T14:23:27.190 回答