将python装饰器应用于所有继承类

时间:2016-02-25 10:37:25

标签: python django

在我的django项目中(django 1.6 - 很快升级到1.9,python 2.7),

我想在我所有项目的模型类(20-30个类)上应用装饰器。 所有这些类都已经从一个名为'LinkableModel'的父类继承而来,这是我为某个(非相关)目的而写的。

现在,我想将类装饰器应用于所有这些模型。 (具体来说,我指的是装饰者'python_2_unicode_compatible':https://docs.djangoproject.com/en/1.9/ref/utils/#django.utils.encoding.python_2_unicode_compatible)。

当我将这个装饰器添加到它们的父类'LinkableModel'时,它不会被继承。 有没有办法将装饰器应用于多个类,而不将其添加到每个类?

(从理论上讲,我甚至不介意这个装饰器是否默认应用于我项目中的所有类......)

代码段:

@python_2_unicode_compatible
class LinkableModel(models.Model):
    ...
    ...
    ...

class MyModel1(LinkableModel):
    ...
    ...
    ...

class MyModel2(LinkableModel):
    ...
    ...
    ...

3 个答案:

答案 0 :(得分:1)

(据我所知)没有简单的方法,因为你不能继承装饰器。

我能想象的最简单的解决方案是:

globals_ = globals()
for name, cls in globals_.items():
    if subclass(cls, Base):
        globals_[name] = decorator(cls)

它只是迭代已经在当前模块中定义的每个全局变量,如果它恰好是继承自Base(或Base本身)的类,则用decorator来装饰它。

请注意,如果符合以下条件,子类将不会被修饰:

  • 在此代码段之后创建
  • 它是在另一个模块中创建的,
  • 它未在全局命名空间中定义。

或者,您可以使用元类:

class Decorate(type):
    def __new__(mcls, name, bases, attrs):
        return decorator(super().__new__(name, bases, attrs))

class Base(metaclass=Decorate):
    pass

当您编写class Base(metaclass=Decorate):时,Python使用Decorate创建Base及其子类。

Decorate所做的就是在返回之前使用decorator修饰类。

如果你使用它,如果你试图继承2个(或更多)类,每个类都有不同的元类,你可能会遇到问题。

答案 1 :(得分:0)

我使用了@GingerPlusPlus的答案,并创建了以下函数,将装饰器应用于类的所有子类:

 def apply_decorator_to_all_subclasses(globals_, base_class, decorator):
    """
    Given a 'globals_' dictionary, a base class, and a decorator - this function applies the decorator to all the defined classes that derive from the base class
    Note!: this function should be called only *after* the subclassess were declared
    :param globals_: the given output of globals(), in the caller's context
    :param base_class: the class whose descendants require the decorator
    :param decorator: the decorator to apply
    """
    for name, cls in globals_.items():
        # Applying only on *class* items, that are descandants of base_class
        if inspect.isclass(cls) and issubclass(cls, base_class) and cls != base_class:
            globals_[name] = decorator(cls)

答案 2 :(得分:0)

现在在Python 3.7中,您可以通过以下方式做到这一点:

  class ParentClass:
      def __init_subclass__(cls, **kwargs):
          return your_decorator(_cls=cls)

它将为ParentClass的每个子类应用装饰器