Мы используем что-то вроде этого:
class CooperativeMeta(type):
def __new__(cls, name, bases, members):
#collect up the metaclasses
metas = [type(base) for base in bases]
# prune repeated or conflicting entries
metas = [meta for index, meta in enumerate(metas)
if not [later for later in metas[index+1:]
if issubclass(later, meta)]]
# whip up the actual combined meta class derive off all of these
meta = type(name, tuple(metas), dict(combined_metas = metas))
# make the actual object
return meta(name, bases, members)
def __init__(self, name, bases, members):
for meta in self.combined_metas:
meta.__init__(self, name, bases, members)
При условии хорошей, современной гигиены реализации метаклассов (где подкласс метаклассов type
и все, что можно сделать в __init__
, выполняется там), это позволяет многимметаклассы, чтобы ладить.
Метаклассы, которые действительно и обязательно выполняют большую часть своей работы в __new__
, будет сложно объединить в любом случае.Вы можете прокрасться здесь одним из них, убедившись, что его класс является первым элементом в множественном наследовании.
Чтобы использовать это, вы просто объявляете:
__metaclass__ = CooperativeMeta
для тех классов, где разныеметаклассы объединяются.
В этом случае, например:
class A:
__metaclass__ = MetaA
class B:
__metaclass__ = MetaB
class Fixed(A, B):
__metaclass__ = CooperativeMeta
Это во много раз больше шансов корректно работать по всем направлениям для разных MetaA и MetaB, чем просто наследовать их вместе для закрытиякомпилятор вверх.
Надеемся, что комментарии объясняют код.Есть только одна хитрая строка, и речь идет об удалении избыточных вызовов для любого заданного __metaclass__
, унаследованного из разных мест, и о том, чтобы классы без явного метакласса могли хорошо играть с другими.Если это кажется чрезмерным, вы можете пропустить его и в своем коде, просто аккуратно упорядочите базовые классы.
Это делает решение трехстрочным и довольно понятным.