Reputation: 1911
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
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
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
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