我正在编写的库大量使用了元类。例如,这是一个基本的单例实现:
class SingletonMeta(type):
_instance = None
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
class ExampleSingleton(metaclass=SingletonMeta):
pass
这很好用,但是当使用多重继承并且另一个类也有一个元类时,就会出现问题。元类在标准库中相当常见。最值得注意的是abc.ABCMeta
。天真的尝试使抽象单例失败:
class AbstractSingleton(ExampleSingleton, abc.ABC):
pass
Traceback (most recent call last):
File "untitled.py", line 25, in <module>
class AbstractSingleton(ExampleSingleton, abc.ABC):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
解决方法很容易-创建一个继承自SingletonMeta
和ABCMeta
的新元类-但对于想要使用我的库的人来说,这确实很烦人。
class AbstractSingletonMeta(SingletonMeta, abc.ABCMeta):
pass
class AbstractSingleton(metaclass=AbstractSingletonMeta):
pass
# no metaclass conflict
解决此问题的最佳方法是什么?
我的一些想法:
SingletonMeta
设为ABCMeta
的子类。AbstractSingletonMeta
。class AbstractSingleton(ExampleSingleton, abc.ABC, metaclass=auto_merge_metaclasses):
)答案 0 :(得分:1)
由于有关必需的元类以及需要组合的内容的所有信息已经存在于基类中,并且已传递给元类调用,因此可以有一个可调用的方法来检查所有的基类及其使用情况元类,并动态创建一个合并的元类。
types
模块中有一些可调用对象,否则可以很容易地为一组基类选择正确的元类。因此,如果所有使用的元类都可以按任意顺序组合,那么下面的函数就可以满足您的需求:
from types import prepare_class
def combine_meta(name, bases, namespace, **kwargs):
metaclasses = {prepare_class(name, (base,))[0] for base in bases}
metaclasses.discard(type)
if len(metaclasses) > 1:
meta_name = '_'.join(mcs.__name__ for mcs in metaclasses)
metaclass = combine_meta(meta_name, tuple(metaclasses), {})
elif len(metaclasses) == 1:
metaclass = metaclasses.pop()
else:
metaclass = type
return metaclass(name, bases, namespace, **kwargs)
我已经在交互式解释器中使用此序列对此进行了测试,并且到目前为止效果良好:
class M1(type): pass
class M2(type): pass
class A(metaclass=M1): pass
class B(metaclass=M2): pass
class C(A, B): pass # This raises a metaclassconflict
class C(A, B, metaclass=combine_meta): pass