在 Python 3 中,是否可以为具有多个基的类动态创建元类?

Sim*_*ing 5 python metaclass

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

诀窍是这些元类本身有一个元类(将其命名为“元元类”),并且此元元类为元类提供一个调用方法,该方法可以在必要时动态创建基本元类的公共子元类。最终,生成一个类,其元类是新的子元类。这是代码:

>>> 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'>
Run Code Online (Sandbox Code Playgroud)

这个例子(当然将语法更改为class C1(metaclass=Meta1)etc)在Python 3中不起作用。

问题 1:我是否正确理解,在 Python 2 中,C3使用第一个基数的元类构造 first ,并且只有在type(C3)不是type(C1)和 的公共子类时才会产生错误type(C2),而在 Python 3 中错误是更早引发的?

问题 2:(如何)可以使上面的示例在 Python 3 中运行吗?我确实尝试使用abc.ABCMetaas metametaclass 的子类,但即使使用自定义__subclasscheck__make issubclass(Meta1, Meta2)return True,创建 C3 仍然会导致错误。

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

mgi*_*son 0

下面的示例显示了 python3.x 中的一些选项。具体来说,C3有一个动态创建的元类,但在很多方面都是显式创建的。 C4有一个在其元类函数中动态创建的元类。 C5只是为了证明它也具有相同的元类属性C4。(我们并没有因为继承而丢失任何东西,如果您使用函数作为元类而不是...,则可能会type发生这种情况)

class Meta1(type):
    def foo(cls):
        print(cls)


class Meta2(type):
    def bar(cls):
        print(cls)


class C1(object, metaclass=Meta1):
    """C1"""


class C2(object, metaclass=Meta2):
    """C2"""


class C3(C1, C2, metaclass=type('Meta3', (Meta1, Meta2), {})):
    """C3"""

def meta_mixer(name, bases, dct):
    cls = type('MixedMeta', tuple(type(b) for b in bases), dct)
    return cls(name, bases, dct)


class C4(C1, C2, metaclass=meta_mixer):
    """C4"""


C1.foo()
C2.bar()

C3.foo()
C3.bar()

C4.foo()
C4.bar()

class C5(C4):
    """C5"""

C5.foo()
C5.bar()
Run Code Online (Sandbox Code Playgroud)

应该指出的是,我们在这里玩火(与您在原始示例中玩火的方式相同)。不能保证元类在协作多重继承中能够很好地发挥作用。如果它们不是为此设计的,那么您很可能在使用它时会遇到错误。如果它们为此而设计的,那么就没有理由进行这种黑客运行时混合:-)。