如何在不显式导入的情况下使类中的新装饰器可用



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

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

我认为这在类装饰器中是不可能的,因为它应用得太晚了。看起来更有前景的选项是使用元类,但我不确定如何使用,我的猜测是必须将some_decorator引入SomeClass的命名空间。

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

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

附录,既然问题已经得到回答。我之所以着眼于简单地在本地导入或定义装饰器,最初的原因是我定义了一个装饰器,只有在对象上初始化了某个容器属性时才能工作,我正在寻找一种方法,将强制执行这一点与装饰器的可用性联系起来。我最终检查了属性是否存在,如果没有在装饰器中初始化它,这很可能是这里较小的错误。

是的,在Python 3中,您可以使用元类__prepare__钩子。它应该返回一个映射,它构成了类主体的本地命名空间的基础:

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

但是您不应该使用此。正如Python的Zen所言:显式优于隐式,在类中引入魔名很容易导致混乱和错误。导入元类与导入decorator没有什么不同,您用另一个名称替换了一个名称。

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

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

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

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

编写一个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的字符串。CCD_ 14被导入,然后CCD_。应用每个装饰器来包装函数。该函数存储在nonlocal中,因此这种导入和修饰只需要在第一次调用该函数时发生。

相关内容

最新更新