如何用python引用可调用的decorator对象中的对象实例?

时间:2013-01-28 23:24:36

标签: python python-2.7 decorator scoping

上下文:

我希望能够装饰功能,以便我可以跟踪他们的统计数据。使用this帖子作为参考,我开始尝试创建自己的可调用装饰器对象。

以下是我最终的结果:

def Stats(fn):
    Class StatsObject(object):
        def __init__(self, fn):
            self.fn = fn
            self.stats = {}

        def __call__(self, obj, *args, **kwargs):
            self.stats['times_called'] = self.stats.get('times_called', 0) + 1
            return self.fn(obj, *args, **kwargs)

    function = StatsObject(fn)
    def wrapper(self, *args **kwargs):
        return function(self, *args, **kwargs)
    return wrapper

Class MockClass(object):
    @Stats
    def mock_fn(self, *args, **kwargs):
        # do things

问题:

这实际上正确地调用了mock_fn函数,但是我没有对包装函数之外的stats对象的引用。即我做不到:

mc = MockClass()
mc.mock_fn()
mc.mock_fn.stats
# HasNoAttribute Exception

然后我尝试更改以下代码,认识到这是一个范围问题:

自:

    function = StatsObject(fn)
    def wrapper(self, *args **kwargs):
        return function(self, *args, **kwargs)
    return wrapper

要:

    function = StatsObject(fn)
    return function

但当然我丢失了自引用(self成为StatsObject实例,obj成为第一个arg,MockClass对象自引用丢失)。

所以我理解为什么第一个问题正在发生,而不是第二个问题。有什么方法可以将MockClass的自引用传递给StatsObject __call__函数吗?

1 个答案:

答案 0 :(得分:2)

函数实际上可以在Python中拥有属性。

def Stats(fn):
    class StatsObject(object):
        def __init__(self, fn):
            self.fn = fn
            self.stats = {}

        def __call__(self, obj, *args, **kwargs):
            self.stats['times_called'] = self.stats.get('times_called', 0) + 1
            return self.fn(obj, *args, **kwargs)

    function = StatsObject(fn)
    def wrapper(self, *args **kwargs):
        return function(self, *args, **kwargs)

    # KEY LINE BELOW: make the StatsObject available outside as "stats_fn"
    wrapper.stats_fn = function

    return wrapper

class MockClass(object):
    @Stats
    def mock_fn(self, *args, **kwargs):
        # do things

关键是将StatsObject实例(您可能会误导,本地命名为function)分配为您从装饰器返回的函数的属性

执行此操作后,self.mock_fn.stats_fn.stats(不是self.mock_fn()!属性在函数上,而不是其返回值)将在MockClass和{{1}的实例中有效将在外面提供。统计信息在MockClass.mock_fn.stats_fn.stats的所有实例中都是全局的(因为装饰器被调用一次,而不是每个实例一次),这可能是你想要的也可能不是。