是否可以在Python 3中为具有多个基础的类动态创建一个元类? [英] Is it possible to dynamically create a metaclass for a class with several bases, in Python 3?

查看:146
本文介绍了是否可以在Python 3中为具有多个基础的类动态创建一个元类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Python 2中,有一个技巧就可以创建一个具有多个基类的类,尽管这些基类具有互不为 子类的元类.

In Python 2, with a trick it is possible to create a class with several bases, although the bases have metaclasses that are not subclass of each other.

诀窍在于,这些元类本身具有一个元类(将其命名为"metameta类"),并且该元元类为元类提供了一种调用方法,该方法可以在必要时动态创建基础元类的公共子元类.最终,产生一个类,其元类是新的子元类.这是代码:

The trick is that these metaclasses have themselves a metaclass (name it a "metametaclass"), and this metametaclass provides the metaclasses with a call method that dynamically creates a common sub-metaclass of the base metaclasses, if necessary. Eventually, a class results whose metaclass is the new sub-metaclass. Here is the code:

>>> class MetaMeta(type):
...     def __call__(mcls, name, bases, methods):
...         metabases = set(type(X) for X in bases)
...         metabases.add(mcls)
...         if len(metabases) > 1:
...             mcls = type(''.join([X.__name__ for X in metabases]), tuple(metabases), {})
...         return mcls.__new__(mcls, name, bases, methods)
... 
>>> class Meta1(type):
...     __metaclass__ = MetaMeta
... 
>>> class Meta2(type):
...     __metaclass__ = MetaMeta
... 
>>> class C1:
...     __metaclass__ = Meta1
... 
>>> class C2:
...     __metaclass__ = Meta2
... 
>>> type(C1)
<class '__main__.Meta1'>
>>> type(C2)
<class '__main__.Meta2'>
>>> class C3(C1,C2): pass
... 
>>> type(C3)
<class '__main__.Meta1Meta2'>

此示例(当然将语法更改为class C1(metaclass=Meta1)等)在Python 3中不起作用.

This example (of course changing the syntax to class C1(metaclass=Meta1) etc) doesn't work in Python 3.

问题1:我是否正确理解在Python 2中使用第一个基元的元类构造了第一个C3,并且仅当type(C3)不是type(C1)type(C2)的常见子类,而在Python 3中,错误是较早产生的?

Question 1: Do I understand correctly that in Python 2, first C3 is constructed, using the metaclass of the first base, and an error would only result if type(C3) were not a common subclass of type(C1) and type(C2), whereas in Python 3 the error is raised earlier?

问题2:(如何)可以使以上示例在Python 3中运行?我确实尝试使用abc.ABCMeta的子类作为元元类,但是即使使用自定义的__subclasscheck__会使issubclass(Meta1, Meta2)返回True,创建C3仍然会导致错误.

Question 2: (How) Is it possible to make the above example work in Python 3? I did try to use a subclass of abc.ABCMeta as metametaclass, but even though using a custom __subclasscheck__ makes issubclass(Meta1, Meta2) return True, the creation of C3 would still result in an error.

注意:当然,我可以通过静态定义Meta1Meta2并将其明确用作C3的元类来使Python 3满意.但是,那不是我想要的.我希望动态创建公共子元类.

Note: Of course I could make Python 3 happy by statically defining Meta1Meta2 and explicitly using it as a metaclass for C3. However, that's not what I want. I want that the common sub-metaclass is created dynamically.

推荐答案

在Python 3中,使用元类时,它必须已经准备好,并且它不知道最后的基础(非元数据)类,以便在那时动态创建一个元类.

In Python 3 at the time the metaclass is used it have to be ready, and it can't know about the bases of the final (non-meta) class in order to dynamically create a metaclass at that point.

但是,除了使事情复杂化(我承认我无法满足您对元元类的需求)之外,您可以简单地将普通类层次结构与super的协同使用一起用于元类. 您甚至可以使用简单的方法动态构建最终的元类 呼叫type:

But instead of complicating things (I confess I could not wrap my head around your need for a meta-meta-class) - you can simply use normal class hierarchy with collaborative use of super for your metaclasses. You can even build the final metaclass dynamically with a simple call to type:

class A(type):
    def __new__(metacls, name, bases,attrs):
        attrs['A'] = "Metaclass A processed"
        return super().__new__(metacls, name, bases,attrs)


class B(type):
    def __new__(metacls, name, bases,attrs):
        attrs['B'] = "Metaclass A processed"
        return super().__new__(metacls, name, bases,attrs)


C = type("C", (A, B), {})

class Example(metaclass=C): pass

并且:

In[47] :Example.A
Out[47]: 'Metaclass A processed'

In[48]: Example.B
Out[48]: 'Metaclass A processed'

如果您的元类并非一开始就设计为协作的,那么创建任何自动方法来组合它们将非常棘手-并且可能会涉及在某些元类中对type.__new__的调用进行猴子修补.构造函数.

If your metaclasses are not designed to be collaborative in the first place, it will be very tricky to create any automatic method to combine them - and it would possibly involve monkey-patching the call to type.__new__ in some of the metaclasses constructors.

对于不需要显式构建C的情况,可以将常规函数用作元类参数,该参数将检查基数并构建动态派生的元类:

As for not needing to explictly build C, you can use a normal function as the metaclass parameter, that will inspect the bases and build a dynamic derived metaclass:

def Auto(name, bases, attrs):
    basemetaclasses = []
    for base in bases:
        metacls = type(base)
        if isinstance(metacls, type) and metacls is not type and not metacls in basemetaclasses:
            basemetaclasses.append(metacls)
    dynamic = type(''.join(b.__name__ for b in basemetaclasses), tuple(basemetaclasses), {})
    return dynamic(name, bases, attrs)

(此代码与您的代码非常相似-但为了保留元类的顺序,我使用了三行显式的for而不是set-这可能很重要)

(This code is very similar to yours - but I used a three-line explicit for instead of a set in order to preserve the metaclass order - which might matter)

您让他们将Auto传递为派生类的元类,但否则它将像您的示例中那样工作:

You have them to pass Auto as a metaclass for derived classes, but otherwise it works as in your example:

In [61]: class AA(metaclass=A):pass

In [62]: class BB(metaclass=B):pass

In [63]: class CC(AA,BB): pass
---------------------------------------------------------------------------
...
TypeError:   metaclass conflict
...

In [66]: class CC(AA,BB, metaclass=Auto): pass

In [67]: type(CC)
Out[67]: __main__.AB

In [68]: CC.A
Out[68]: 'Metaclass A processed'

这篇关于是否可以在Python 3中为具有多个基础的类动态创建一个元类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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