Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining Python decorators for a complete module

I have a module which contains a lot of functions (more than 25). I want to add a common decorator function to each of these functions. The normal way to do is to add a @decorator line above each function, but I was wondering if there is a better way to do it? Probably I can declare a global decorator at the top of the module or something else?

Note that since I am using someone else's code, I want to minimize the number of lines changed, so modifying the module is not ideal for me.

Thanks.

like image 870
Rajat Avatar asked Jan 21 '12 08:01

Rajat


People also ask

How do you write a method decorator in Python?

To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function.

What is Python decorator explain in detail?

A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.

Can Python function have multiple decorators?

Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.

What are different types of decorators in Python?

In fact, there are two types of decorators in Python — class decorators and function decorators — but I will focus on function decorators here. Before we get into the fun details of how a basic decorator works and how to implement your own decorators, let's see why we need them in the first place.


2 Answers

If your decorator is called my_decorator

### Decorate all the above functions
import types
for k,v in globals().items():
    if isinstance(v, types.FunctionType):
        globals()[k] = my_decorator(v)

You could also apply this to the module after importing it

import othermodule
import types
for k,v in vars(othermodule).items():
    if isinstance(v, types.FunctionType):
        vars(othermodule)[k] = my_decorator(v)
like image 54
John La Rooy Avatar answered Oct 06 '22 02:10

John La Rooy


I think applying a decorator en-masse such that it's not obvious where you will go looking to find out about the function (at its definition) is generally a bad idea. Explicit is better than implicit, and all that.

If you want to apply the decorator to some third party module's functions, without modifying the third-party code, here is how I would do it:

# my_wrapper_module.py

import some_module
import functools

def some_decorator(func):
    @functools.wraps(func):
    def wrapper(*args, **kwargs):
        ...
    return wrapper

FUNCTION_NAMES = [
    'some_func_1',
    'some_func_2',
    'some_func_3',
    ...
]

for name in FUNCTION_NAMES:
    globals()[name] = some_decorator(getattr(some_module, name))

And then use these functions elsewhere by doing from my_wrapper_module import some_func_2, etc.

For me, this has the following advantages:

  1. No need to modify the third-party source file
  2. It is clear from the call site that I should go look at my_wrapper_module to see what I'm calling, and that I'm not using the undecorated versions of the functions
  3. It is clear from my_wrapper_module what functions are being exported, that they originally come from some_module, and that they all have the same decorator applied
  4. Any code that imports some_module directly isn't silently and inexplicably affected; this could be particularly important if the third-party code is more than one module

But if what you're trying to do is hack a third-party library so that internal calls are affected, then this is not what you want.

like image 39
Ben Avatar answered Oct 06 '22 00:10

Ben