如何在 class 中提供新装饰器而不显式导入它们?

How to make new decorators available within a class without explicitly importing them?

是否可以修改 class 以使某个方法装饰器可用,而不必显式导入它也不必为其添加前缀 (@something.some_decorator):

class SomeClass:

    @some_decorator
    def some_method(self):
        pass

我认为 class 装饰器不可能做到这一点,因为应用得太晚了。看起来更有希望的选项是使用元 class,但我不确定如何,我的猜测是我必须将 some_decorator 引入到 SomeClass.[= 的命名空间中18=]

感谢@MartijnPieters 指出 staticmethodclassmethod 是内置的。我原以为它们会成为 type 机器的一部分。

明确地说,我没有任何明确的用例,我只是想知道这是否完全可行。

附录,现在问题已得到解答。我不局限于简单地在本地导入或定义装饰器的最初原因是,我定义了一个装饰器,该装饰器只有在对象上初始化了某个容器属性时才起作用,并且我正在寻找一种方法来强制执行此操作装饰器的可用性。我最终检查了该属性是否存在,如果不存在则在装饰器中对其进行初始化,这很可能是较小的问题。

是的,在Python3你可以使用metaclass __prepare__ hook。它应该是 return 一个映射,它构成了 class 主体的本地名称空间的基础:

def some_decorator(f):
    print(f'Decorating {f.__name__}')
    return f

class meta(type):
    @classmethod
    def __prepare__(mcls, name, bases, **kw):
        return {'some_decorator': some_decorator}

class SomeClass(metaclass=meta):
    @some_decorator
    def some_method(self):
        pass

运行 以上产生

Decorating some_method

但是你不应该使用这个。正如 Zen of Python 所述:显式优于隐式,并且在您的 classes 中引入魔法名称很容易导致混淆和错误。导入 metaclass 与导入装饰器没有什么不同,你用一个名字替换了另一个名字。

class 装饰器仍然可以在 class 主体创建后将其他装饰器应用于 class 上的方法。 @decorator 语法只是 name = decorator(decorated_object) 的语法糖,您以后总是可以通过使用 name = decorator(name) 或在 class 上下文中应用装饰器,如 cls.name = decorator(cls.name) .如果你需要挑选和选择这应该适用于哪些方法,你可以选择标准,如方法名称,或方法上设置的属性,或方法的文档字符串等。或者直接在方法上使用装饰器。

据我所知,元类可以做到这一点。您可能需要以某种方式让元类可以使用装饰器,可能是通过导入,然后您可以将它们包含在准备好的命名空间中:

class includewraps(type):
    def prepare(*args):
        from functools import wraps
        return {'wraps': wraps}


class haswraps (metaclass = includewraps):
    # wraps is available in this scope

编写一个装饰器,它接受一个字符串列表并在第一次调用该函数之前导入它们。这避免了显式导入,直到需要它们之前的最后一刻。与此处的所有其他答案一样,这可能表明您的代码应该重组。

from functools import wraps
from importlib import import_module

def deferred(*names):
    def decorator(f):
        # this will hold the fully decorated function
        final_f = None

        @wraps(f)
        def wrapper(*args, **kwargs):
            nonlocal final_f

            if final_f is None:
                # start with the initial function
                final_f = f

                for name in names:
                    # assume the last . is the object to import
                    # import the module then get the object
                    mod, obj = name.rsplit('.', 1)
                    d = getattr(import_module(mod), obj)
                    # decorate the function and keep going
                    final_f = d(final_f)

            return final_f(*args, **kwargs)

        return wrapper

    return decorator
# for demonstration purposes, decorate with a function defined after this
# assumes this file is called "example.py"
@deferred('example.double')
def add(x, y):
    return x + y

def double(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        return 2 * f(*args, **kwargs)

    return wrapper

if __name__ == '__main__':
    print(add(3, 6))  # 18

deferred 的参数应该是 'path.to.module.decorator 形式的字符串。导入 path.to.module,然后从模块中检索 decorator。每个装饰器都用于包装函数。该函数存储在 nonlocal 中,因此只需要在第一次调用该函数时进行导入和装饰。