2

So, I have a code snippet to count the number of function calls, and while browsing for potential optimal solutions, I came across a similar question posted here a few years ago:

is there a way to track the number of times a function is called?

One of the solutions listed in the thread above matched mine, but there was a subtle difference. When I posted my solution and asked about the potential pitfalls in my code, my comment was deleted even though mine was a solution to the question. So, I am hoping this one isn't closed as a duplicate, because frankly I don't know where to turn to.

Here was my solution:

def counter(func): counter.count=0 def wrap(*args,**kwargs): counter.count += 1 print "Number of times {} has been called so far {}".format(func.__name__,counter.count) func(*args,**kwargs) return wrap @counter def temp(name): print "Calling {}".format(name) 

My counter is defined as an attribute to the decorator 'counter', instead of the wrapper function 'wrap'. The code works as currently defined. But, is there a scenario where it may fail? Am I overlooking something here?

3
  • Sure. If you decorate foo, call it a few million times, then define a new function afterwards named bar and decorate that too, then your previous counts of foo will disappear. Commented Jul 20, 2018 at 20:08
  • 1
    There's a better implementations in the Python Decorator Library in which the decorator is a class which would make instances of it (i.e. uses of it) independent. Commented Jul 20, 2018 at 20:43
  • Martineau's answer seems to be the best one out there. No matter where I define the counter variable (inside or outside of 'wrap'), it doesn't count the total number of calls across multiple functions. The call count resets when the decorator is applied to a new function. Commented Aug 13, 2018 at 19:36

2 Answers 2

3

If you use this decorator on two separate functions, they'll share the same call count. That's not what you want. Also, every time this decorator is applied to a new function, it will reset the shared call count.

Aside from that, you've also forgotten to pass through the return value of func in wrap, and you can't stick an unescaped line break in the middle of a non-triple-quoted string literal like that.

Sign up to request clarification or add additional context in comments.

Comments

1

With slight modification to your code you can make your counter decorator independent:

def counter(func, counter_dict={}): counter_dict[func]=0 def wrap(*args,**kwargs): counter_dict[func] += 1 print("Number of times {} has been called so far {}".format(func.__name__, counter_dict[func])) return func(*args,**kwargs) return wrap @counter def temp1(name): print("Calling temp1 {}".format(name)) @counter def temp2(name): print("Calling temp2 {}".format(name)) temp1('1') temp1('2') temp2('3') 

Prints:

Number of times temp1 has been called so far 1 Calling temp1 1 Number of times temp1 has been called so far 2 Calling temp1 2 Number of times temp2 has been called so far 1 Calling temp2 3 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.