一个类继承了2个不同的元类(abcmeta和用户定义的meta)



我有一个class1,需要从2个不同的元类,即Meta1和abc继承。ABCMeta

当前实现:

Meta1的实现:

class Meta1(type):
    def __new__(cls, classname, parent, attr):
        new_class = type.__new__(cls, classname, parent, attr)
        return super(Meta1, cls).__new__(cls, classname, parent, attr)

class1Abstract

class class1Abstract(object):
    __metaclass__ = Meta1
    __metaclass__ = abc.ABCMeta

mainclass的实现

class mainClass(class1Abstract):
    # do abstract method stuff

我知道这是错误的实现两个不同的元两次。

我改变了metclass加载的方式(几次尝试),我得到了这个TypeError:调用元类基

时出错
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

I ran out of ideas…


EDITED 1

我尝试了这个解决方案,它的工作,但mainClass不是class1Abstract的实例

print issubclass(mainClass, class1Abstract) # true
print isinstance(mainClass, class1Abstract) # false

class1Abstract

class TestMeta(Meta1):
    pass

class AbcMeta(object):
    __metaclass__ = abc.ABCMeta
    pass

class CombineMeta(AbcMeta, TestMeta):
    pass

class class1Abstract(object):
    __metaclass__ = CombineMeta
    @abc.abstractmethod
    def do_shared_stuff(self):
        pass
    @abc.abstractmethod
    def test_method(self):
        ''' test method '''

mainClass的实现

class mainClass(class1Abstract):
    def do_shared_stuff(self):
        print issubclass(mainClass, class1Abstract) # True
        print isinstance(mainClass, class1Abstract) # False

由于mainClass继承自抽象类,python应该抱怨test_method没有在mainClass中实现。但是它不会报错因为print isinstance(mainClass, class1Abstract) # False

dir(mainClass)没有

['__abstractmethods__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry']

帮助!


EDITED 2

class1Abstract

CombineMeta = type("CombineMeta", (abc.ABCMeta, Meta1), {})
class class1Abstract(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def do_shared_stuff(self):
        pass
    @abc.abstractmethod
    def test_method(self):
        ''' test method '''

mainClass的实现

class mainClass(class1Abstract):
    __metaclass__ = CombineMeta
    def do_shared_stuff(self):
        print issubclass(mainClass, class1Abstract) # True
        print isinstance(mainClass, class1Abstract) # False

dir(mainClass)现在有抽象方法的魔法方法

['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', 'do_shared_stuff', 'test_method']

但是python不会警告test_method没有被实例化

帮助!

在Python中,每个类只能有一个元类,不能有很多。然而,通过混合这些元类所做的,也可以实现类似的行为(比如有多个元类)。

让我们从简单的开始。我们自己的元类,只是给一个类添加了一个新属性:

class SampleMetaClass(type):
  """Sample metaclass: adds `sample` attribute to the class"""
  def __new__(cls, clsname, bases, dct):
    dct['sample'] = 'this a sample class attribute'
    return super(SampleMetaClass, cls).__new__(cls, clsname, bases, dct)
class MyClass(object):
  __metaclass__ = SampleMetaClass
print("SampleMetaClass was mixed in!" if 'sample' in MyClass.__dict__ else "We've had a problem here")

打印"SampleMetaClass was mixed in!",所以我们知道基本的元类工作得很好。

现在,在另一边,我们需要一个抽象类,最简单的是:

from abc import ABCMeta, abstractmethod
class AbstractClass(object):
  __metaclass__ = ABCMeta
  @abstractmethod
  def implement_me(self):
    pass
class IncompleteImplementor(AbstractClass):
  pass
class MainClass(AbstractClass):
  def implement_me(self):
    return "correct implementation in `MainClass`"
try:
  IncompleteImplementor()
except TypeError as terr:
  print("missing implementation in `IncompleteImplementor`")
MainClass().implement_me()

打印"IncompleteImplementor中缺失的实现",随后是"MainClass中的正确实现"。因此,抽象类也可以正常工作。

现在,我们有两个简单的实现,我们需要将两个元类的行为混合在一起。这里有多个选项

选项1 -子类化

可以实现SampleMetaClass作为ABCMeta的子类-元类也是类,可以继承它们!

class SampleMetaABC(ABCMeta):
  """Same as SampleMetaClass, but also inherits ABCMeta behaviour"""
  def __new__(cls, clsname, bases, dct):
    dct['sample'] = 'this a sample class attribute'
    return super(SampleMetaABC, cls).__new__(cls, clsname, bases, dct)

现在,我们改变AbstractClass定义中的元类:

class AbstractClass(object):
  __metaclass__ = SampleMetaABC
  @abstractmethod
  def implement_me(self):
    pass
# IncompleteImplementor and MainClass implementation is the same, but make sure to redeclare them if you use same interpreter from the previous test

并再次运行我们的两个测试:

try:
  IncompleteImplementor()
except TypeError as terr:
  print("missing implementation in `IncompleteImplementor`")
MainClass().implement_me()
print("sample was added!" if 'sample' in IncompleteImplementor.__dict__ else "We've had a problem here")
print("sample was added!" if 'sample' in MainClass.__dict__ else "We've had a problem here")

这仍然会打印出IncompleteImplementor没有正确实现,而MainClass是正确实现的,并且两者现在都添加了sample类级别属性。这里需要注意的是,元类的Sample部分也成功地应用于IncompleteImplementor(好吧,没有理由不这样做)。

正如预期的那样,isinstanceissubclass仍然可以正常工作:

print(issubclass(MainClass, AbstractClass)) # True, inheriting from AbtractClass
print(isinstance(MainClass, AbstractClass)) # False as expected - AbstractClass is a base class, not a metaclass
print(isinstance(MainClass(), AbstractClass)) # True, now created an instance here

选项2 -组合元类

事实上,问题本身就有这个选项,它只需要一个小的修复。将新的元类声明为几个更简单的元类的组合,以混合它们的行为:

SampleMetaWithAbcMixin = type('SampleMetaWithAbcMixin', (ABCMeta, SampleMetaClass), {})

和前面一样,更改AbstractClass的元类(同样,IncompleteImplementorMainClass不会更改,但如果在同一个解释器中重新声明它们):

class AbstractClass(object):
  __metaclass__ = SampleMetaWithAbcMixin
  @abstractmethod
  def implement_me(self):
    pass

从这里开始,运行相同的测试应该产生相同的结果:ABCMeta仍然工作并确保@abstractmethod -s被实现,SampleMetaClass添加了sample属性。

我个人更喜欢后一种选择,原因与我通常更喜欢组合而不是继承相同:最终在多个(元)类之间需要的组合越多-使用组合就越简单。

关于元类的更多信息

最后,我读过的关于元类最好的解释是这个答案:什么是Python中的元类?

默认情况下,python只在您尝试实例化类时抱怨类具有抽象方法,而不是在您创建类时。这是因为类的元类仍然是ABCMeta(或其子类型),所以允许有抽象方法。

要得到你想要的,你需要写你自己的元类,当它发现__abstractmethods__不是空的时候引发一个错误。这样,当一个类不再允许使用抽象方法时,您必须显式声明。

from abc import ABCMeta, abstractmethod
class YourMeta(type):
    def __init__(self, *args, **kwargs):
        super(YourMeta, self).__init__(*args, **kwargs)
        print "YourMeta.__init__"
    def __new__(cls, *args, **kwargs):
        newcls = super(YourMeta, cls).__new__(cls, *args, **kwargs)
        print "YourMeta.__new__"
        return newcls
class ConcreteClassMeta(ABCMeta):
    def __init__(self, *args, **kwargs):
        super(ConcreteClassMeta, self).__init__(*args, **kwargs)
        if self.__abstractmethods__:
            raise TypeError("{} has not implemented abstract methods {}".format(
                self.__name__, ", ".join(self.__abstractmethods__)))
class CombinedMeta(ConcreteClassMeta, YourMeta):
    pass
class AbstractBase(object):
    __metaclass__ = ABCMeta
    @abstractmethod
    def f(self):
        raise NotImplemented
try:
    class ConcreteClass(AbstractBase):
        __metaclass__ = CombinedMeta
except TypeError as e:
    print "Couldn't create class --", e
class ConcreteClass(AbstractBase):
    __metaclass__ = CombinedMeta
    def f(self):
        print "ConcreteClass.f"
assert hasattr(ConcreteClass, "__abstractmethods__")
c = ConcreteClass()
c.f()
输出:

YourMeta.__new__
YourMeta.__init__
Couldn't create class -- ConcreteClass has not implemented abstract methods f
YourMeta.__new__
YourMeta.__init__
ConcreteClass.f

不需要设置两个元类:Meta1应该从abc.ABCMeta继承

在您编辑的代码(1和2)中,您几乎完成了。唯一错误的是你如何使用isinstance。您想要检查类实例(在本例中为self)是否是给定类(class1Abstract)的实例。例如:

class mainClass(class1Abstract):
def do_shared_stuff(self):
    print issubclass(mainClass, class1Abstract) # True
    print isinstance(self, class1Abstract) # True

最新更新