How to create a (transparent) decorator with status information?

Ian Kelly ian.g.kelly at gmail.com
Mon Apr 18 15:07:58 EDT 2011


On Mon, Apr 18, 2011 at 6:47 AM, Timo Schmiade <the_isz at gmx.de> wrote:
> Hi all,
>
> I'm currently occupying myself with python's decorators and have some
> questions as to their usage. Specifically, I'd like to know how to
> design a decorator that maintains a status. Most decorator examples I
> encountered use a function as a decorator, naturally being stateless.
>
> Consider the following:
>
> def call_counts(function):
>  @functools.wraps(function):
>  def wrapper(*args, **kwargs):
>    # No status, can't count #calls.
>    return function(*args, **kwargs)
>  return wrapper

In the simple case, just store the state on the wrapper function itself:

def call_counts(function):
  @functools.wraps(function)
  def wrapper(*args, **kwargs):
    wrapper.num_calls += 1
    return function(*args, **kwargs)
  wrapper.num_calls = 0
  return wrapper

@call_counts
def f():
  pass

f()
f()
print(f.num_calls)

> * The maintained status is not shared among multiple instances of the
>  decorator. This is unproblematic in this case, but might be a problem
>  in others (e.g. logging to a file).

If you want the state to be shared, you should probably store it in an
object and use an instance method as the decorator:

class CallCounter(object):
  def __init__(self):
    self.num_calls = 0
  def call_counts(self, function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
      self.num_calls += 1
      return function(*args, **kwargs)
    return wrapper

call_counter = CallCounter()
@call_counter.call_counts
def f1_with_shared_counts():
  pass

@call_counter.call_counts
def f2_with_shared_counts():
  pass

Cheers,
Ian



More information about the Python-list mailing list