如何在python中做一个条件装饰器?

puruo6ea  于 2023-02-28  发布在  Python
关注(0)|答案(7)|浏览(145)

有条件地修饰函数是可能的吗?例如,我想用一个定时器函数(timeit)修饰函数foo(),但是只有当doing_performance_analysis的条件是True时,如下所示:

if doing_performance_analysis:
      @timeit
      def foo():
        """
        Do something, e.g. sleep, and let timeit 
        return the time it takes
        """
        time.sleep(2)
    else:
      def foo():
        time.sleep(2)
polkgigr

polkgigr1#

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

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参数作为实参存储在示例上,直到应用装饰器为止。

wlzqhblo

wlzqhblo2#

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

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

if doing_performance_analysis:
    foo = timeit(foo)
atmip9wb

atmip9wb3#

不如这样:

def foo():
   ...

if doing_performance_analysis:
   foo = timeit(foo)

我想您甚至可以将其 Package 到一个装饰器中,该装饰器将接受一个布尔标记和另一个装饰器,并且仅在标记设置为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():
   ...
bz4sfanl

bz4sfanl4#

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

lzfw57am5#

如果你想在每次调用函数的时候都做检查,blckknight的答案很好。但是如果你有一个设置,你可以读取一次,永远不会改变,你可能不想检查设置每次装饰函数被调用。在我们工作的一些高性能守护进程中,我编写了一个装饰器,它在第一次加载python文件时检查一次设置文件,并决定是否应该 Package 它或不是。
以下是一个示例

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装饰器添加到函数中,将在加载时检查一次,以查看您是否在此服务器、环境如果没有,那么它不会改变生产环境或其他您关心性能的环境中函数的执行。

6rqinv9w

6rqinv9w6#

以下是对我起作用的方法:

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

euoag5mw7#

有点晚了,但是这篇文章对我很有帮助,我想添加一些我觉得有用的东西。我想做一个条件装饰器,装饰器接受一些参数。你可以用partial很干净地完成这个。

from functools import partial

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

这允许您部分地应用装饰器所接受的参数,并且仍然使其有条件地应用。
以下面的例子为例,我们有一个装饰器,它将n添加到某个函数的结果中:

def add_n(func, n=1):
    def wrapper_add_n(*args, **kwargs):
        return func(*args, **kwargs) + n
    return wrapper_add_n

我们可以使用修改后的条件装饰器来添加n参数。

@conditional_decorator(add_n, True, n=3)
def multiply(a, b):
    return a * b

print(multiply(1, 2))

它打印5

相关问题