如何在python中使用条件装饰器

如何在python中使用条件装饰器,python,conditional,decorator,python-decorators,Python,Conditional,Decorator,Python Decorators,是否可以有条件地装饰函数。例如,我想用一个计时器函数(timeit)来修饰函数foo()),只做性能分析是正确的(参见下面的psuedo代码) 那么: def foo(): ... if doing_performance_analysis: foo = timeit(foo) 我想您甚至可以将其包装到一个装饰器中,该装饰器将接受一个布尔标志和另一个装饰器,并且仅在标志设置为True时应用后者: def cond_decorator(flag, dec): def deco

是否可以有条件地装饰函数。例如,我想用一个计时器函数(
timeit
)来修饰函数
foo()
),只做性能分析是
正确的
(参见下面的psuedo代码)

那么:

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():
   ...

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

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

if doing_performance_analysis:
    foo = timeit(foo)

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

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)

在这里,
\uuuu call\uu
方法与第一个示例中返回的
decorator()
嵌套函数的作用相同,在应用decorator之前,这里的closed-over
dec
condition
参数将作为实例上的参数存储。

Blckknght的答案非常好,如果您想在每次调用函数时进行检查,但是,如果您有一个可以读取一次且从不更改的设置,那么您可能不希望在每次调用修饰函数时都检查该设置。在我们工作的一些高性能守护进程中,我编写了一个decorator,它在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 decorator添加到函数将在加载时检查一次,以查看您是否在此服务器、环境上计时,如果没有,则不会改变生产环境或其他您关心性能的环境中功能的执行。

以下是对我有效的方法:

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()
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})

谢谢评论部分没有格式化,所以我在原始回复中添加了示例代码。你能解释一下为什么不调用定时函数吗?decorator是在导入时应用的,所以当时没有参考实例变量。你必须为此编写一个不同的装饰器,一个在调用时检查self的装饰器。超出此Q和注释格式的范围。:-)如果我想在基于类的方法(即Django基于类的视图)上使用它,我将如何使用它。在那里,我们必须使用
方法\u decorator
。如何使此代码与之兼容?@pythonenthsusiast:只需传入调用结果:
@method\u decorator(conditional\u decorator(timeit,doing\u performance\u analysis))
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})