问题
What is the proper way of inheriting from ABC if class have metaclass specified?
Straightforward attempt
class KindMeta(type):
...
class Kind(ABC, metaclass=KindMeta):
...
resulted in TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Inheriting ABCMeta in KindMeta seems kind of wrong, because this mcs
is also used to create nonabstract classes.
Creating custom ABC inherited from KindMeta sounds well neither.
Is there a better way of dealing with it? or which of the solutions is more pythonic and should be preferred?
回答1:
You have to create a second metaclass, inheritng from both your orignal metaclass and the abc.ABCMeta
and use that metaclass as the metaclass on the classes you want.
If your metaclass is correctly constructed, using super()
calls in all (special) methods it implements, it as straightforward as:
import abc
...
class KindMeta(type):
...
class CombinedMeta(KindMeta, abc.ABCMeta):
pass
class Kind(ABC, metaclass=CombinedMeta):
...
If your metaclass is not making use of super()
, but calling hardcoded type
methods, you have to change it to do so. For some methods like __prepare__
and __call__
, it makes sense to not call the correspondent
super()
method depending on what you are doing, and I think there is no need to run the correspondent methods in ABCMeta
.
And, of course, that is only needed if you don't want or can't change the metaclass you already have, or if you want your metaclass to be used in other classes that are not using ABC -
Otherwise, you can just make your own metaclass inherit from ABCMeta
itself - no need to create a third metaclass to combine both:
import abc
...
class KindMeta(abc.ABCMeta):
...
If, on the other hand, you are using the metaclass + class construction machinery of Python to create objects that are "not quite classes" (like for example the zope.interface
package does to create interfaces), you have to decide (1) if it makes sense to use that along abc.ABC
at all, and second, if the correspondent method in the ABCMeta have to be run (usually yes, if you need the functionality). In that case you have to customize your metaclass appropriately - which may include to force combining the classes with multiple-inheritance (even if you could just inherit from ABCMeta) to prevent it from calling type.__new__
(if that is your intent):
class KindMeta(type):
def __new__(mcls, name, bases, ns, **kw):
cls = ExistingClassRegistry[name]
return cls
class CombinedMeta(abc.ABCMeta, KindMeta):
# note the reversed order - ABCMeta will run first
# and call super().__new__ which runs KindMeta.__new__ and
# does not forward to type.__new__
pass
来源:https://stackoverflow.com/questions/57349105/python-abc-inheritance-with-specified-metaclass