在 Python 2 中,通过一个技巧,可以创建一个具有多个基类的类,尽管基类具有不是彼此子类的元类。

诀窍是这些元类本身有一个元类(将其命名为“元元类”),并且这个元元类为元类提供了一个调用方法,如果需要,该方法动态地创建基元类的公共(public)子元类。最终,产生一个类,其元类是新的子元类。这是代码:

>>> 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 中不起作用。

问题 1: 我是否正确理解在 Python 2 中,首先使用第一个基的元类构造 C3,并且仅当 type(C3) 不是 type(C1)type(C2) 的公共(public)子类时才会导致错误,而在 Python 3 中错误是提早提出的?

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

注意: 当然,我可以通过静态定义 Meta1Meta2 并将其显式用作 C3 的元类来使 Python 3 满意。然而,这不是我想要的。我希望动态创建公共(public)子元类。

最佳答案

在 Python 3 中,使用元类时它必须准备好,并且它无法知道最终(非元)类的基础,以便在那时动态创建元类。

但是,不要使事情复杂化(我承认我无法理解您对元元类的需求)——您可以简单地使用普通的类层次结构,并协同使用 super 为您的元类。
您甚至可以使用简单的动态构建最终元类
调用 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__ 的调用进行猴子修补。

至于不需要显式构建 C ,您可以使用普通函数作为元类参数,它将检查基础并构建动态派生元类:
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 以保留元类顺序 - 这可能很重要)

您让它们将 Auto 作为派生类的元类传递,但除此之外,它的工作方式与您的示例一样:
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 - 是否可以在 Python 3 中为具有多个基类的类动态创建元类?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40031906/

10-16 03:53