在 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/