代码之家  ›  专栏  ›  技术社区  ›  oulenz

如何使新的装饰器在类中可用,而不显式导入它们?

  •  6
  • oulenz  · 技术社区  · 7 年前

    @something.some_decorator ):

    class SomeClass:
    
        @some_decorator
        def some_method(self):
            pass
    

    我认为这在类装饰器中是不可能的,因为应用得太晚了。看起来更有希望的选择是使用元类,但我不确定如何使用,我想我必须这样做 some_decorator 被引入的命名空间 SomeClass .

    感谢@MartijnPieters指出这一点 staticmethod classmethod 是内置的。我本以为他们会成为 type 机械

    明确地说,我没有任何明确的用例,我只是好奇这是否可能。

    补遗,现在问题已经得到回答。我不仅仅是在本地导入或定义装饰器,最初的原因是我定义了一个装饰器,该装饰器只有在对象上初始化了某个容器属性时才能工作,我正在寻找一种将强制执行与装饰器的可用性联系起来的方法。我最后检查了属性是否存在,如果没有在装饰器中初始化它,这很可能是这里的小问题。

    3 回复  |  直到 7 年前
        1
  •  7
  •   Martijn Pieters    7 年前

    是的,在Python 3中,您可以使用 metaclass __prepare__ hook . 它将返回一个映射,并形成类主体的本地命名空间的基础:

    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 国家: 显式优于隐式 ,并在课堂上引入魔术名称 很容易导致混淆和错误

    在创建类主体后,类装饰器仍然可以将其他装饰器应用于类上的方法。这个 @decorator 语法只是 name = decorator(decorated_object) ,您可以随时使用 name = decorator(name) ,或在类上下文中,如 cls.name = decorator(cls.name)

        2
  •  3
  •   Sam Hartman    7 年前

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

    class includewraps(type):
        def prepare(*args):
            from functools import wraps
            return {'wraps': wraps}
    
    
    class haswraps (metaclass = includewraps):
        # wraps is available in this scope
    
        3
  •  2
  •   davidism Stephen J.    7 年前

    编写一个decorator,在第一次调用函数之前获取字符串列表并导入它们。这避免了显式导入,直到需要它们之前的最后一刻。与这里的所有其他答案一样,这可能表明您的代码应该重新构造。

    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 因此,这种导入和装饰只需要在第一次调用函数时发生。