【问题标题】:Metaclass mixing or chaining元类混合或链接
【发布时间】:2015-01-18 01:17:21
【问题描述】:

考虑一下代码:

class Meta(type):
    def __init__(cls, name, bases, attrs):
        def method(self):
            print('generated method call')
        cls.method = method
        super(Meta, cls).__init__(name, bases, attrs)


class A(object):
    __metaclass__ = Meta

    def method(self):
        raise NotImplementedError


def decorator(fn):
    def wrapper(*args, **kwargs):
        print('decorator call')
        return fn(*args, **kwargs)

    return wrapper

class Decorator(object):
    """Mysterious mixin that should wrap `method` with `decorator`
    at class generation time. And this effect should work on subclasses too!
    """
    def __call__(self, cls):
        cls.method = decorator(cls.method)
        return cls

@Decorator()
class B(A):
    pass

B().method() # outputs "decorated call generated method call"

class D(B):
    pass

D().method() # outputs only "generated method call"

这里我有一个基类A,它有一个生成method 的元类。 比我有Decorator 类装饰器,它为装饰类的method 添加了一些效果。

它在装饰类B 上完美运行,但它的效果不可继承,因此D 不会继承该效果。

这就是我想要实现的 - 学习如何制作可继承的类装饰器。或者它可以被称为元类混合。

如何实现这个效果?我对元类的所有尝试都因metaclass conflict 错误而失败。

【问题讨论】:

  • 简要提示:class DecoratorMeta(Meta); class B(A): __metaclass__ = DecoratorMeta
  • @o11c 如果我有多个 mixin 怎么办?
  • 完全可以在函数中本地定义一个新的元类
  • @o11c 你的意思是 - 生成元类?

标签: python metaprogramming decorator metaclass python-decorators


【解决方案1】:

BD 两个类都有自己对 method 的引用。那是因为每次创建新类时,您都会在 Meta__init__ 方法中分配它。您可以通过查看__dict__ 属性(如print(D.__dict__))进行检查。

要实现继承行为,类D不能包含自己的method,这样会从父类B中获取,而method确实被修饰了。

综上所述,我向您提出以下解决方案:

def decorator(cls):
    def _dec(original_method):
        def _method(*args, **kwargs):
            print('{cls_name} decorated: '.format(cls_name=cls.__name__), end='')
            return original_method(*args, **kwargs)
        return _method
    cls.method = _dec(cls.method)
    return cls


class A():

    def method(self):
        pass


@decorator
class B(A):
    pass


class D(B):
    pass

if __name__ == '__main__':
    A().method() or print('A')
    B().method() or print('B')
    D().method() or print('D')

这段代码的执行结果如下:

A
B decorated: B
B decorated: D

【讨论】:

    猜你喜欢
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多