Counting python method calls within another method

后端 未结 3 1598
囚心锁ツ
囚心锁ツ 2020-11-29 10:11

I\'m actually trying doing this in Java, but I\'m in the process of teaching myself python and it made me wonder if there was an easy/clever way to do this with wrappers or

相关标签:
3条回答
  • 2020-11-29 10:54

    This defines a decorator to do it:

    def count_calls(fn):
        def _counting(*args, **kwargs):
            _counting.calls += 1
            return fn(*args, **kwargs)
        _counting.calls = 0
        return _counting
    
    @count_calls
    def foo(x):
        return x
    
    def bar(y):
        foo(y)
        foo(y)
    
    bar(1)
    print foo.calls
    
    0 讨论(0)
  • 2020-11-29 11:06

    After your response - here's a way with a decorator factory...

    import inspect
    
    def make_decorators():
        # Mutable shared storage...
        caller_L = []
        callee_L = []
        called_count = [0]
        def caller_decorator(caller):
            caller_L.append(caller)
            def counting_caller(*args, **kwargs):
                # Returning result here separate from the count report in case
                # the result needs to be used...
                result = caller(*args, **kwargs)
                print callee_L[0].__name__, \
                       'was called', called_count[0], 'times'
                called_count[0] = 0
                return result
            return counting_caller
    
        def callee_decorator(callee):
            callee_L.append(callee)
            def counting_callee(*args, **kwargs):
                # Next two lines are an alternative to
                # sys._getframe(1).f_code.co_name mentioned by Ned...
                current_frame = inspect.currentframe()
                caller_name = inspect.getouterframes(current_frame)[1][3]
                if caller_name == caller_L[0].__name__:
                    called_count[0] += 1
                return callee(*args, **kwargs)
            return counting_callee
    
        return caller_decorator, callee_decorator
    
    caller_decorator, callee_decorator = make_decorators()
    
    @callee_decorator
    def foo(z):
        #do something
        return ' foo result'
    
    @caller_decorator
    def bar(x,y):
        # complicated algorithm/logic simulation...
        for i in xrange(x+y):
            foo(i)
        foobar = 'some result other than the call count that you might use'
        return foobar
    
    
    bar(1,1)
    bar(1,2)
    bar(2,2)
    

    And here's the output (tested with Python 2.5.2):

    foo was called 2 times
    foo was called 3 times
    foo was called 4 times
    
    0 讨论(0)
  • 2020-11-29 11:08

    Sounds like almost the textbook example for decorators!

    def counted(fn):
        def wrapper(*args, **kwargs):
            wrapper.called += 1
            return fn(*args, **kwargs)
        wrapper.called = 0
        wrapper.__name__ = fn.__name__
        return wrapper
    
    @counted
    def foo():
        return
    
    >>> foo()
    >>> foo.called
    1
    

    You could even use another decorator to automate the recording of how many times a function is called inside another function:

    def counting(other):
        def decorator(fn):
            def wrapper(*args, **kwargs):
                other.called = 0
                try:
                    return fn(*args, **kwargs)
                finally:
                    print '%s was called %i times' % (other.__name__, other.called)
            wrapper.__name__ = fn.__name__
            return wrapper
        return decorator
    
    @counting(foo)
    def bar():
        foo()
        foo()
    
    >>> bar()
    foo was called 2 times
    

    If foo or bar can end up calling themselves, though, you'd need a more complicated solution involving stacks to cope with the recursion. Then you're heading towards a full-on profiler...

    Possibly this wrapped decorator stuff, which tends to be used for magic, isn't the ideal place to be looking if you're still ‘teaching yourself Python’!

    0 讨论(0)
提交回复
热议问题