【问题标题】:how to do a conditional decorator in python如何在python中做一个条件装饰器
【发布时间】:2012-05-30 06:59:23
【问题描述】:

是否可以有条件地装饰函数。例如,我想用定时器函数(timeit)装饰函数foo(),只有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)  

【问题讨论】:

    标签: python conditional decorator python-decorators


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢!评论部分没有格式化,所以我将示例代码添加到您的原始回复中。你能解释一下为什么不调用计时函数吗?
    • 装饰器是在导入时应用的,因此此时不会查询实例变量。你必须为此编写一个不同的装饰器,一个在调用时检查自我的装饰器。超出此 Q 和评论格式的范围。 :-)
    • 如果我想在基于类的方法(即基于 Django 类的视图)上使用它,我将如何使用它。在那里我们必须使用method_decorator。如何让这段代码与之兼容?
    • @PythonEnthusiast:只需传入调用结果:@method_decorator(conditional_decorator(timeit, doing_performance_analysis))
    【解决方案2】:

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

    def foo():
       # whatever
       time.sleep(2)
    
    if doing_performance_analysis:
        foo = timeit(foo)
    

    【讨论】:

      【解决方案3】:

      怎么样:

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

      【讨论】:

        【解决方案4】:
        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()
        

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

            这对我有用:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-03-03
              • 2012-12-04
              • 1970-01-01
              • 2017-11-16
              • 2019-10-02
              • 1970-01-01
              • 2015-02-22
              • 2011-09-03
              相关资源
              最近更新 更多