元类混合还是链接? [英] Metaclass Mixin or Chaining?

查看:42
本文介绍了元类混合还是链接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以链接元类?

我有一个类Model,它使用__metaclass__=ModelBase处理其名称空间字典.我将从中继承并绑定"另一个元类,这样它就不会遮蔽原始的元类.

I have class Model which uses __metaclass__=ModelBase to process its namespace dict. I'm going to inherit from it and "bind" another metaclass so it won't shade the original one.

第一种方法是继承class MyModelBase(ModelBase):

MyModel(Model):
    __metaclass__ = MyModelBase # inherits from `ModelBase`

但是是否可以像mixin一样将它们链接起来,而无需显式子类化?

But is it possible just to chain them like mixins, without explicit subclassing? Something like

class MyModel(Model):
    __metaclass__ = (MyMixin, super(Model).__metaclass__)

...甚至更好:创建一个MixIn,它将使用使用它的类的直接父级中的__metaclass__:

... or even better: create a MixIn that will use __metaclass__ from the direct parent of the class that uses it:

class MyModel(Model):
    __metaclass__ = MyMetaMixin, # Automagically uses `Model.__metaclass__`


原因:为了在扩展现有应用程序方面具有更大的灵活性,我想创建一个全局机制来挂接到Django中的ModelForm,...定义的过程中,以便可以在运行时进行更改.


The reason: For more flexibility in extending existing apps, I want to create a global mechanism for hooking into the process of Model, Form, ... definitions in Django so it can be changed at runtime.

一种通用机制比使用回调混合实现多个元类要好得多.

A common mechanism would be much better than implementing multiple metaclasses with callback mixins.

在您的帮助下,我终于设法提出了一个解决方案:元类MetaProxy.

With your help I finally managed to come up to a solution: metaclass MetaProxy.

这个想法是:创建一个元类,该元类调用一个回调来修改正在创建的类的名称空间,然后在__new__的帮助下,将其转变为父级之一的元类.

The idea is: create a metaclass that invokes a callback to modify the namespace of the class being created, then, with the help of __new__, mutate into a metaclass of one of the parents

#!/usr/bin/env python
#-*- coding: utf-8 -*-

# Magical metaclass
class MetaProxy(type):
    """ Decorate the class being created & preserve __metaclass__ of the parent

        It executes two callbacks: before & after creation of a class, 
        that allows you to decorate them.

        Between two callbacks, it tries to locate any `__metaclass__` 
        in the parents (sorted in MRO). 
        If found — with the help of `__new__` method it
        mutates to the found base metaclass. 
        If not found — it just instantiates the given class.
        """

    @classmethod
    def pre_new(cls, name, bases, attrs):
        """ Decorate a class before creation """
        return (name, bases, attrs)

    @classmethod
    def post_new(cls, newclass):
        """ Decorate a class after creation """
        return newclass

    @classmethod
    def _mrobases(cls, bases):
        """ Expand tuple of base-classes ``bases`` in MRO """
        mrobases = []
        for base in bases:
            if base is not None: # We don't like `None` :)
                mrobases.extend(base.mro())
        return mrobases

    @classmethod
    def _find_parent_metaclass(cls, mrobases):
        """ Find any __metaclass__ callable in ``mrobases`` """
        for base in mrobases:
            if hasattr(base, '__metaclass__'):
                metacls = base.__metaclass__
                if metacls and not issubclass(metacls, cls): # don't call self again
                    return metacls#(name, bases, attrs)
        # Not found: use `type`
        return lambda name,bases,attrs: type.__new__(type, name, bases, attrs)

    def __new__(cls, name, bases, attrs):
        mrobases = cls._mrobases(bases)
        name, bases, attrs = cls.pre_new(name, bases, attrs) # Decorate, pre-creation
        newclass = cls._find_parent_metaclass(mrobases)(name, bases, attrs)
        return cls.post_new(newclass) # Decorate, post-creation



# Testing
if __name__ == '__main__':
    # Original classes. We won't touch them
    class ModelMeta(type):
        def __new__(cls, name, bases, attrs):
            attrs['parentmeta'] = name
            return super(ModelMeta, cls).__new__(cls, name, bases, attrs)

    class Model(object):
        __metaclass__ = ModelMeta
        # Try to subclass me but don't forget about `ModelMeta`

    # Decorator metaclass
    class MyMeta(MetaProxy):
        """ Decorate a class

            Being a subclass of `MetaProxyDecorator`,
                it will call base metaclasses after decorating
            """
        @classmethod
        def pre_new(cls, name, bases, attrs):
            """ Set `washere` to classname """
            attrs['washere'] = name
            return super(MyMeta, cls).pre_new(name, bases, attrs)

        @classmethod
        def post_new(cls, newclass):
            """ Append '!' to `.washere` """
            newclass.washere += '!'
            return super(MyMeta, cls).post_new(newclass)

    # Here goes the inheritance...
    class MyModel(Model):
        __metaclass__ = MyMeta
        a=1
    class MyNewModel(MyModel):
        __metaclass__ = MyMeta # Still have to declare it: __metaclass__ do not inherit
        a=2
    class MyNewNewModel(MyNewModel):
        # Will use the original ModelMeta
        a=3

    class A(object):
        __metaclass__ = MyMeta # No __metaclass__ in parents: just instantiate
        a=4
    class B(A): 
        pass # MyMeta is not called until specified explicitly



    # Make sure we did everything right
    assert MyModel.a == 1
    assert MyNewModel.a == 2
    assert MyNewNewModel.a == 3
    assert A.a == 4

    # Make sure callback() worked
    assert hasattr(MyModel, 'washere')
    assert hasattr(MyNewModel, 'washere')
    assert hasattr(MyNewNewModel, 'washere') # inherited
    assert hasattr(A, 'washere')

    assert MyModel.washere == 'MyModel!'
    assert MyNewModel.washere == 'MyNewModel!'
    assert MyNewNewModel.washere == 'MyNewModel!' # inherited, so unchanged
    assert A.washere == 'A!'

推荐答案

我不认为您可以像这样将它们链接起来,我也不知道该如何工作.

I don't think you can chain them like that, and I don't know how that would work either.

但是您可以在运行时创建新的元类并使用它们.但这是一个可怕的黑客. :)

But you can make new metaclasses during runtime and use them. But that's a horrid hack. :)

zope.interface的功能类似,它有一个顾问元类,它将在构造后对该类做一些事情.如果已经有一个元类,它将完成的其中一项操作是将前一个元类设置为完成后的元类.

zope.interface does something similar, it has an advisor metaclass, that will just do some things to the class after construction. If there was a metclass already, one of the things it will do it set that previous metaclass as the metaclass once it's finished.

(但是,除非必要,否则不要做这些事情,否则不要以为很有趣.)

(However, avoid doing these kinds of things unless you have to, or think it's fun.)

这篇关于元类混合还是链接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆