在decorator中声明的函数访问变量

时间:2015-11-07 05:57:34

标签: python python-2.7 logging python-decorators

我想记录很多指标,例如执行函数需要多长时间。我希望解决方案是轻量级的。添加装饰器并完成。也许90%的时间都是这样的:

@LogMetrics
def my_business_logic(var1):
  metrics.inc_counter('counter_a')
  print 'done'

但是,我也希望能够访问指标对象并在10%的时间内执行此类操作:

class LogMetrics(object):

  def __init__(self, module=None):
    self.module = module


  def __call__(self, target):

    def wrapped(*args, **kwargs):
      # set up metrics
      metrics = Metrics(module=module)

      metrics.start_timer()
      try:
        result = target(*args, **kwargs)
      except Exception as e:
        metrics.set_fault()
        raise e
      finally:
        metrics.stop_timer()
        metrics.push()

      return result

    return wrapped

到目前为止,我有一个处理第一个场景的装饰器:

keywords = ET.Element('keywords')
for string_ in strings: # < -- your strings
    string = ET.SubElement(keywords, 'string')
    string.text = string_

ET.dump(keywords)

如何支持方案2?我想做这样的事情answer,但共识似乎不是这样做的。请帮忙......

2 个答案:

答案 0 :(得分:1)

唯一不涉及全局对象或禁止魔法的解决方案是将您的度量对象作为可选参数传递给您的函数(我假设如果您可以编辑它,那么您可以更改它签名):

class LogMetrics(object):

  ...

  def __call__(self, target):

    def wrapped(*args, **kwargs):
      # set up metrics
      metrics = Metrics(module=module)
      kwargs['metrics'] = metrics  # add metrics object to kwargs
      metrics.start_timer()
      try:
        result = target(*args, **kwargs)
      except Exception as e:
        metrics.set_fault()
        raise e
      finally:
        metrics.stop_timer()
        metrics.push()
      return result
    return wrapped


@LogMetrics
def my_business_logic(var1, metrics=None):
  if metrics is not None:
      metrics.inc_counter('counter_a')
  print 'done'

顺便说一下,您确定要在Metrics内而不是wrapped内初始化__call__吗?

答案 1 :(得分:0)

您可以向函数添加属性。我做了一个非常简单的例子(在Python3中)。 display: none装饰器会向原始函数添加add_counter属性(您将添加_counter对象)。每次调用后,此示例中的计数器都会递增。最后,在装饰函数中添加metrics属性以允许访问计数器。

_orig_func