-1

I am having an object I cant touch, but would like to log the method calls. I don't really understand decorators, but I was wondering if there is a more elegant way to do something like this

import pprint

 class myTest(object):
    def __init__(self):
        self.a = 1
    def test(self, **kwargs):
        for k, v in kwargs.items():            
            setattr(self, k, v)   

def wrap(o, method, **kwargs):
    for k, v in kwargs.items():
        print '%s set to %s' %(k, v)
        getattr(o, method)(**kwargs)


o = myTest()
d = {'b':2,'c': 3}

wrap(o, 'test', **d)
pprint.pprint(vars(o))

such that I can decorate a regular method call and do it somehow like this:

@mydecorator
o.test(**d)

and would get a similar result. Then I would not have to replace all the method calls in the script

chrise
  • 4,039
  • 3
  • 39
  • 74
  • possible duplicate of [How can I make a chain of function decorators in Python?](http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python) – sundar nataraj Sep 11 '14 at 08:52
  • You need to apply decorator at function definition, not at function call. Try to read [this article](http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/) – stalk Sep 11 '14 at 08:53

1 Answers1

1

The question mentioned in the comments has some excellent explainations of decorators, so you should read that first - How to make a chain of function decorators?

As to your specific question. As you can't touch the myTest object definition, you can just change it's methods at runtime! You can do this just to your instance:

import pprint

class myTest(object):
    def __init__(self):
        self.a = 1
    def test(self, **kwargs):
        for k, v in kwargs.items():            
            setattr(self, k, v)   

def log_calls(fn):
    def wrapped(*args, **kwargs):
        print '%s called with args %s and kwargs %s' % (repr(fn), repr(args), repr(kwargs))
        fn(*args, **kwargs)
    return wrapped

o = myTest()
o.test = log_calls(o.test) # equivalent to applying the decorator at method
                           # definition, but only applies to this instance of myTest

d = {'b':2, 'c':3}
o.test(**d)
pprint.pprint(vars(o))

Outputs:

<bound method myTest.test of <__main__.myTest object at 0x0000000002EE07F0>> called with args () and kwargs {'c': 3, 'b': 2}
{'a': 1, 'b': 2, 'c': 3, 'test': <function wrapped at 0x0000000002EDDEB8>}

Or modify the class itself:

myTest.test = log_calls(myTest.test)
o = myTest()

d = {'b':2, 'c':3}
o.test(**d)
pprint.pprint(vars(o))

This is called monkey patching - you can find some more info here What is a monkey patch?

Community
  • 1
  • 1
Peter Gibson
  • 19,086
  • 7
  • 60
  • 64