Themerius
Themerius

Reputation: 1911

Deactivate function with decorator

Is it possible to "deactivate" a function with a python decorator? Here an example:

cond = False

class C:

    if cond:
        def x(self): print "hi"

    def y(self): print "ho"

Is it possible to rewrite this code with a decorator, like this?:

class C:

    @cond
    def x(self): print "hi"

    def y(self): print "ho"

Background: In our library are some dependencies (like matplotlib) optional, and these are only needed by a few functions (for debug or fronted). This means on some systems matplotlib is installed on other systems not, but on both should run the (core) code. Therefor I'd like to disable some functions if matplotlib is not installed. Is there such elegant way?

Upvotes: 7

Views: 5446

Answers (3)

knobi
knobi

Reputation: 912

removing methods from a class seems strange to me. Why not use try / except

plot_avail = True   
try:
    import matplotlib   
except:
    plot_avail = False

then in your function(s)

def x(self):
    if not plot_avail: return

So no call ends up in: class has not attribute X

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1122102

You can turn functions into no-ops (that log a warning) with a decorator:

def conditional(cond, warning=None):
    def noop_decorator(func):
        return func  # pass through

    def neutered_function(func):
        def neutered(*args, **kw):
            if warning:
                log.warn(warning)
            return
        return neutered

    return noop_decorator if cond else neutered_function

Here conditional is a decorator factory. It returns one of two decorators depending on the condition.

One decorator simply leaves the function untouched. The other decorator replaces the decorated function altogether, with one that issues a warning instead.

Use:

@conditional('matplotlib' in sys.modules, 'Please install matplotlib')
def foo(self, bar):
    pass

Upvotes: 22

l4mpi
l4mpi

Reputation: 5149

Martijns answer deals with tunring the functions into noops, I'm going to explain how you can actually remove them from the class - which is probably overkill, I'd settle for a variation of Martijns answer that throws some sort of exception. But anyways:

You could use a class decorator to remove the affected functions from the class. This one takes a bool and a list of attributes to remove:

def rm_attrs_if(cond, attrs):

    if not cond:
        return lambda c: c #if the condition is false, don't modify the class

    def rm_attrs(cls):
        d = dict(cls.__dict__) #copy class dict
        for attr in attrs:
            del d[attr]        #remove all listed attributes
        return type(cls.__name__, cls.__bases__, d) #create and return new class

    return rm_attrs

Use it like this:

@rm_attrs_if(something == False, ["f1", "f2"])
class X():
    def f1(): pass
    def f2(): pass
    def f3(): pass

Upvotes: 2

Related Questions