代码之家  ›  专栏  ›  技术社区  ›  job

计算另一个方法中的python方法调用数

  •  12
  • job  · 技术社区  · 15 年前

    我实际上是在用Java做这件事,但是我正在教我自己的Python,它让我怀疑是否有一个简单的方法来用包装纸之类的东西来做。

    我想知道在另一个方法中调用特定方法的次数。例如:

    def foo(z):
        #do something
        return result
    
    def bar(x,y):
        #complicated algorithm/logic involving foo
        return foobar
    

    因此,对于每个具有不同参数的对bar的调用,我想知道调用foo的次数,可能使用如下输出:

    >>> print bar('xyz',3)
    foo was called 15 times
    [results here]
    >>> print bar('stuv',6)
    foo was called 23 times
    [other results here]
    

    编辑:我意识到我可以在酒吧里拍打一个柜台,然后在我回来的时候把它扔掉,但是如果你能用包装纸做些魔法来完成同样的事情,那就太酷了。这也意味着我可以在其他地方重用相同的包装器,而不必修改方法中的任何代码。

    3 回复  |  直到 7 年前
        1
  •  20
  •   jmd_dk    7 年前

    听起来就像是装饰师的教科书例子!

    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
    

    您甚至可以使用另一个修饰器自动记录在另一个函数中调用函数的次数:

    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
    

    如果 foo bar 但是,最终可能会调用自己,您需要一个更复杂的解决方案,涉及堆栈来处理递归。然后你将向一个全面的探查器…

    如果你还在自学巨蟒的话,这种包装好的装饰材料可能不太适合用来施魔法!

        2
  •  7
  •   Ned Batchelder    15 年前

    这定义了一个要执行此操作的装饰器:

    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
    
        3
  •  2
  •   Anon    15 年前

    在你的回答之后-这是一个装饰工厂的方法…

    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)
    

    下面是输出(用python 2.5.2测试):

    foo was called 2 times
    foo was called 3 times
    foo was called 4 times