在Python2中,使用一个技巧可以创建一个具有多个基类的类,尽管这些基类的元类互为而不是子类。在
诀窍在于这些元类本身有一个元类(将其命名为“元元类”),并且该元元类为元类提供了一个调用方法,该方法可以在必要时动态创建基元类的公共子元类。最终,一个类的结果是其元类是新的子元类。代码如下:
>>> 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)
等)在python3中不起作用。在
问题1:我是否正确地理解,在Python2中,第一个C3
是使用第一个基的元类构造的,并且只有当{
问题2:(如何)使上述示例在Python3中工作?我确实尝试过使用abc.ABCMeta
的子类作为元元类,但是即使使用自定义的__subclasscheck__
使issubclass(Meta1, Meta2)
返回{
注意:当然,我可以通过静态定义Meta1Meta2
并显式地将其作为C3
的元类来让Python3高兴起来。然而,这不是我想要的。我希望公共子元类是动态创建的。在
在95%的情况下,应该可以使用python3.6中引入的机制,因为PEP 447可以使用特殊的新钩子来完成元类的大部分工作。在这种情况下,您不需要组合元类,因为您的钩子可以调用super,并且它们的行为是由于继承而组合的。在
至于你的一般情况,我相信梅森是对的,你可能把事情搞得太复杂了。我还没有看到pep447没有涉及到的组合元类的情况。在
下面是一个例子,它展示了python3.x中的一些选项,具体来说,}所具有的相同元类属性。(如果您使用函数作为元类而不是
C3
有一个动态创建的元类,但是在很多方面都是显式的。C4
有一个元类,它是在元类的函数中动态创建的。C5
只是为了证明它也具有{type
…),那么继承不会导致任何损失需要注意的是,我们在这里使用fire(与您在原始示例中使用fire的方式相同)。不能保证元类在合作多重继承中能很好地发挥作用。如果它们不是为它而设计的,那么很有可能在某个点使用它时遇到bug。如果它们是为它而设计的,那么就没有理由进行这种骇人听闻的运行时混合:-)。在
在python3中,在使用元类时,它必须准备好,并且它不知道最终(非元)类的基,以便在那时动态地创建一个元类。在
但是,与其让事情复杂化(我承认我无法完全理解您对元类的需求),您可以简单地使用普通的类层次结构,协同使用
super
作为元类。 你甚至可以用一个简单的 调用type
:以及:
^{pr2}$如果你的元类一开始就不是为了协作而设计的,那么创建任何自动方法来组合它们是非常困难的——而且可能涉及到在一些元类构造函数中对
type.__new__
的调用进行monkey修补。在至于不需要显式地构建
C
,可以使用一个普通函数作为元类参数,它将检查基并构建一个动态派生元类:(这段代码与您的代码非常相似-但我使用了三行显式
for
而不是set
以保持元类顺序-这可能很重要)您可以让它们将Auto作为派生类的元类传递,但在其他情况下,它的工作方式与您的示例相同:
相关问题 更多 >
编程相关推荐