是否有一种 pythonic 方法可以跳过子类方法的装饰?

Is there a pythonic way to skip decoration on a subclass' method?

我有一个 class,它使用来自另一个库的装饰器来装饰一些方法。具体来说,class subclasses flask-restful 资源,用 httpauth.HTTPBasicAuth().login_required() 修饰 http 方法,并对模型服务做一些合理的默认设置。

在大多数子classes 上,我希望应用装饰器;因此我宁愿删除它也不愿将其添加到 subclasses.

我的想法是有一个执行操作的私有方法和一个修饰的 public 方法。可以通过覆盖 public 方法调用私有方法而不装饰此覆盖来避免装饰的影响。下面是模拟示例。

我很想知道是否有更好的方法来做到这一点。在 python 中是否有 'cancelling decorators' 的快捷方式可以产生这种效果?

或者你能推荐一个更好的方法吗?

其他一些问题对此有合适的答案,例如Is there a way to get the function a decorator has wrapped?。但我的问题是关于更广泛的设计 - 我对 anypythonic 方法感兴趣 运行 没有装饰效果的装饰方法中的操作。例如。我的例子就是这样一种方式,但可能还有其他方式。

def auth_required(fn):
    def new_fn(*args, **kwargs):
        print('Auth required for this resource...')
        fn(*args, **kwargs)
    return new_fn

class Resource:
    name = None

    @auth_required
    def get(self):
        self._get()

    def _get(self):
        print('Getting %s' %self.name)

class Eggs(Resource):
    name = 'Eggs'

class Spam(Resource):
    name = 'Spam'

    def get(self):
        self._get()
        # super(Spam, self)._get()

eggs = Eggs()
spam = Spam()

eggs.get()
# Auth required for this resource...
# Getting Eggs

spam.get()
# Getting Spam

另一个常见的选项是让装饰函数保留一份可以访问的原始函数的副本:

def auth_required(fn):
    def new_fn(*args, **kwargs):
        print('Auth required for this resource...')
        fn(*args, **kwargs)
    new_fn.original_fn = fn
    return new_fn

现在,对于任何已修饰的函数,您可以访问其 original_fn 属性以获取原始未修饰函数的句柄。

在这种情况下,您可以定义某种类型的调度程序,它可以进行普通函数调用(当您对装饰器行为感到满意时),或者在您希望避免装饰器行为时调用 thing.original_fn

您提出的方法也是构造它的有效方法,我的建议是否 "better" 取决于您正在处理的其余代码、需要阅读它的人以及其他类型的取舍。

Flask-HTTPAuth uses functools.wrapslogin_required 装饰器中:

def login_required(self, f):
    @wraps(f)
    def decorated(*args, **kwargs):
        ...

从 Python 3.2 开始,调用 update_wrapper,您可以通过 __wrapped__:

访问原始函数

To allow access to the original function for introspection and other purposes (e.g. bypassing a caching decorator such as lru_cache()), this function automatically adds a __wrapped__ attribute to the wrapper that refers to the function being wrapped.

如果您正在编写自己的装饰器,如您的示例所示,您也可以使用 @wraps 获得相同的功能(以及保留文档字符串等)。

另见 Is there a way to get the function a decorator has wrapped?

I am curious to know if there's a better way to do this. Is there a shortcut for 'cancelling decorators' in python that gives this effect?

使用undecorated 库。它挖掘了所有装饰器和 returns 只是原始函数。文档应该是不言自明的,基本上你只需调用:undecorated(your_decorated_function)