python - 1 个类继承 2 个不同的元类(abcmeta 和用户定义的元)

标签 python metaprogramming metaclass

我有一个 class1 需要从两个不同的元类继承,即 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

主类的实现

class mainClass(class1Abstract):
    # do abstract method stuff

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

我改变了加载metclass的方式(几次尝试),我明白了 TypeError:调用元类库时出错

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

我的想法用完了......


已编辑 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

目录(mainClass) 没有

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

帮助!


已编辑 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) 现在有了 abstractmethod 的魔法方法

['__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 中的正确实现”。因此,抽象类也可以正常工作。

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

选项 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 属性。

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

有关元类的更多信息

最后,我读过的关于元类的最佳解释是这样的答案: What is a metaclass in Python?

关于python - 1 个类继承 2 个不同的元类(abcmeta 和用户定义的元),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31379485/

相关文章:

python - 使用 Airflow,MsSqlOperator 是否接受来自 SQL Server 的响应?

python - 如何从 odoo 8 中的另一个函数字段定义 one2many 字段?

python - 在函数上定义 __getattr__ 和 __getitem__ 无效

python - 在类级别使用装饰器注册方法

macros - Elixir 宏和 bind_quoted

python - 如何在python元编程中使用实例方法?

python - 使用 Python 3 的平均年龄计算器

python - 图片在OpenCV中的文字

iOS/objective-C : Correct Way of Obtaining Meta Class Object

python - 为什么 abc.ABCMeta 抽象实例化检查不适用于 `list` 和 `dict` 的衍生物?