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