Sunday, January 19, 2014

Python Decorators - The correct way to do it

Was going through Graham Dumpleton's blog post - how you implemented your python decorator is wrong. Simple points that were discussed were -


  • Decorators can be functions as well as Classes.
    As a class -  

    class function_wrapper(object):
        def __init__(self, wrapped):
            self.wrapped = wrapped
        def __call__(self, *args, **kwargs):
            return self.wrapped(*args, **kwargs) 
    @function_wrapper
    def function():
        pass 

    As a function 

    def function_wrapper(wrapped):
        def _wrapper(*args, **kwargs):
            return wrapped(*args, **kwargs)
        return _wrapper 
    @function_wrapper
    def function():
        pass 

  • Use the functools.wraps decorator , it only preserves original functions __name__ and __class__
    In functions 

    import functools 
    def function_wrapper(wrapped):
        @functools.wraps(wrapped)
        def _wrapper(*args, **kwargs):
            return wrapped(*args, **kwargs)
        return _wrapper 
    @function_wrapper
    def function():
        pass 

    In classes, use the update_wrapper method- 

    import functools 
    class function_wrapper(object):
        def __init__(self, wrapped):
            self.wrapped = wrapped
            functools.update_wrapper(self, wrapped)
        def __call__(self, *args, **kwargs):
            return self.wrapped(*args, **kwargs)
  • Python 2.7 preserves Argument specification ( inspect.getargspec ) only in functional decorators, not in class based ones.
  • Doesnt preserve function source code for inspection ( inspect.getsource ) 
  • Cannot apply decorators on top of other decorators that are implemented as descriptors.

No comments:

Post a Comment