【问题标题】:Deactivate function with decorator使用装饰器停用功能
【发布时间】:2013-07-30 12:58:50
【问题描述】:

是否可以使用 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)是可选的,只有少数函数需要这些依赖项(用于调试或前端)。这意味着在某些系统上 matplotlib 未安装在其他系统上,但在两者上都应该运行(核心)代码。因此,如果未安装 matplotlib,我想禁用某些功能。有这么优雅的方式吗?

【问题讨论】:

  • 您为什么需要这样做?额外的功能几乎不占用资源。如果禁用这些方法是否会引发属性错误?
  • 定义disable。如果你的意思是把函数变成空操作,这可以通过返回 lambda *_: None 的装饰器轻松完成。
  • 在最好的情况下,它们应该消失或只记录未安装 matplotlib 的警告。

标签: python decorator python-decorators


【解决方案1】:

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

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

然后在你的函数中

def x(self):
    if not plot_avail: return

所以没有调用结束:类没有属性 X

【讨论】:

    【解决方案2】:

    您可以使用装饰器将函数变成无操作(记录警告):

    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
    

    【讨论】:

    • 是否也可以将matplotlib注入func?
    • 这里的注入是什么意思?你不能改变func 本身;你可以替换func(用一个调用func的包装器,或者不调用,以产生结果)但你不能改变func本身的代码。
    • 或者更确切地说,如果你真的想去那里,你可以,但这需要反编译和/或 AST 树转换。您通常会尽量避免去那里。
    • 是的,我认为这样一点语法糖就够了。
    【解决方案3】:

    Martijns 答案涉及将函数转换为 noops,我将解释如何将它们从类中实际删除 - 这可能是矫枉过正,我会满足于 Martijns 答案的变体,它会引发某种异常.但无论如何:

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

    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
    

    【讨论】:

    • 为什么要在装饰器中新建一个类?我认为做for attr in attrs: delattr(cls, attr)然后返回cls会更容易。
    • @SvenMarnach 习惯的力量,我猜 - 在 python 中动态创建新类是我习惯的,删除现有类的属性不是。这种方法的优点是您可以使用装饰器从现有的类中非破坏性地派生类,例如B = rm_attrs_if(flag, attrs)(A) 创建一个新类 B 而不改变 A。
    猜你喜欢
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 2021-12-05
    • 2023-03-28
    • 2021-12-15
    • 2011-07-29
    • 2020-09-08
    相关资源
    最近更新 更多