Functions can actually themselves have attributes in 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
The key line is assigning the StatsObject
instance (which you've, perhaps misleadingly, locally named function
) as an attribute of the function which you return from the decorator.
Once you do this, self.mock_fn.stats_fn.stats
(not self.mock_fn()
! The attribute is on the function, not its return value) will work within an instance of MockClass
, and MockClass.mock_fn.stats_fn.stats
will be available outside. The statistics will be global across all instances of MockClass
(since the decorator is called once, not once per instance), which may or may not be what you want.