在另一个方法中计算 python 方法调用

Posted

技术标签:

【中文标题】在另一个方法中计算 python 方法调用【英文标题】:Counting python method calls within another method 【发布时间】:2009-08-19 18:19:20 【问题描述】:

我实际上正在尝试在 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]

编辑:我意识到我可以在吧台内放一个柜台,然后当我返回时将其倾倒,但如果你可以用包装器做一些魔法来完成同样的事情,那就太酷了。这也意味着我可以在其他地方重用相同的包装器,而无需修改方法内的任何代码。

【问题讨论】:

在Java中你可以用一个方面来做,使用cflow检查调用方法和call匹配方法来计数,然后实现周围的建议以增加每个匹配方法的计数器(可能使用方法签名和计数的映射) 或只计算调用,只需匹配 execution 而不是使用 cflowcall 我打算给你介绍一个 Pyhon 代码覆盖工具 (coverage.py),因为它肯定包含对你想做的事情有用的技术。但后来作者本人(嗨,内德!)回答了你的问题。无论如何,你可能想看看nedbatchelder.com/code/coverage。 你好 df!具有讽刺意味的是,我没有提到coverage.py... 【参考方案1】:

听起来几乎是装饰器的教科书示例!

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

如果foobar 最终可以调用自己,那么您需要一个涉及堆栈的更复杂的解决方案来应对递归。然后你将走向一个全面的分析器......

如果您仍在“自学 Python”,那么这种包装的装饰器可能不是理想的地方,它往往被用于魔法!

【讨论】:

我在第二个例子中得到foo was called 0 times【参考方案2】:

这定义了一个装饰器来做这件事:

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

【讨论】:

那么,所有修饰函数是否共享这个全局变量?看起来他们是这样的。 >>> print CALLS 2 >>> @count_calls ... def foobar(y): ... return y ... >>> foobar(1) 1 >>> print CALLS 1 我更改了代码以保留函数属性的计数,所以现在它们将被分开。 这行得通,但它不会计算 ONLY bar 调用 foo 的次数。您必须添加一些逻辑来确定 foo 的调用者,我猜这是可能的,但我不知道该怎么做。 感谢 Ned,这真的很接近。但我认为三联画是对的。我在想一些更像是 bar 上的包装器,它以 foo 作为参数。然后 bar 包装器将包装 foo 以计数调用,并在 bar 执行后打印计数。 我没有完整的代码,但这可能会有所帮助:sys._getframe(1).f_code.co_name 是您的调用者的姓名。【参考方案3】:

在您回复后 - 这是装饰工厂的一种方式...

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

【讨论】:

我意识到我可以在 bar 方法中的某处添加一个计数器,然后在我返回时将其打印出来,但我想知道是否可以在不实际更改 bar 中的任何代码的情况下这样做。 啊 - 明白了 - 我编辑了一个新的答案,与一家工厂合作,让两个装饰器合作。

以上是关于在另一个方法中计算 python 方法调用的主要内容,如果未能解决你的问题,请参考以下文章

一个方法中的hashMap的值,怎么在另一个类中调用啊?

使用命令行参数在另一个 Python 脚本中调用一个 Python 脚本

OCMock 期望在另一个方法中调用一个方法

如何在 java 类方法中连接到我的数据库 sql 并在另一个类中调用它?

使用 Mockery 模拟在另一个静态方法中调用的静态方法

如何在另一个 AppDomain 中调用方法