用装饰器取消激活功能

时间:2013-07-30 11:37:54

标签: python decorator python-decorators

是否可以"停用"一个python装饰器的函数?这是一个例子:

cond = False

class C:

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

    def y(self): print "ho"

是否可以使用装饰器重写此代码,如下所示?:

class C:

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

    def y(self): print "ho"

背景:在我们的库中有一些依赖项(比如matplotlib)是可选的,这些只需要几个函数(用于debug或fronted)。这意味着在某些系统上matplotlib不安装在其他系统上,但两者都应该运行(核心)代码。因此,如果未安装matplotlib,我想禁用某些功能。有这么优雅的方式吗?

3 个答案:

答案 0 :(得分:16)

您可以使用装饰器将函数转换为no-ops(记录警告):

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

这里conditional是装饰工厂。它根据条件返回两个装饰器中的一个。

一位装饰师只是让这个功能不受影响。另一个装饰器完全替换了装饰的功能,而不是发出警告。

使用:

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

答案 1 :(得分:2)

Martijns回答将函数调入noops的问题,我将解释你如何将它们从类中删除 - 这可能有点过分,我会解决一个Martijns答案的变种,它会引发某种异常。但无论如何:

您可以使用类装饰器从类中删除受影响的函数。这个需要一个bool和一个要删除的属性列表:

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

像这样使用:

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

答案 2 :(得分:1)

从类中删除方法对我来说似乎很奇怪。为什么不使用try / except

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

然后在您的函数中

def x(self):
    if not plot_avail: return

所以没有通话结束:类没有属性X