【问题标题】:Python: why do I need super().__init__() call in metaclasses?Python:为什么我需要在元类中调用 super().__init__()?
【发布时间】:2020-11-06 07:45:03
【问题描述】:

我有一个问题:为什么我需要在元类中调用 super().--init--()?因为元类是类的工厂,我认为我们不需要调用初始化来制作类 Shop 的对象。或者使用 super().--init-- 我们初始化类? (因为我的 IDE 说,我应该调用它。但是没有 super().--init-- 什么也没有发生,我的班级工作没有错误)。 你能给我解释一下,为什么? 提前致谢!

class Descriptor:
    _counter = 0

    def __init__(self):
        self.attr_name = f'Descriptor attr#{Descriptor._counter}'
        Descriptor._counter += 1

    def __get__(self, instance, owner):
        return self if instance is None else instance.__dict__[self.attr_name]

    def __set__(self, instance, value):
        if value > 0:
            instance.__dict__[self.attr_name] = value
        else:
            msg = 'Value must be > 0!'
            raise AttributeError(msg)

class Shop():
    weight = Descriptor()
    price = Descriptor()


    def __init__(self, name, price, weight):
        self.name = name
        self.price = price
        self.weight = weight
        
    def __repr__(self):
        return f'{self.name}: price - {self.price} weight - {self.weight}'

    def buy(self):
        return self.price * self.weight


class Meta(type):
    def __init__(cls, name, bases, attr_dict):
        super().__init__(name, bases, attr_dict) # <- this is that func. call
        for key, value in attr_dict.items():
            if isinstance(value, Descriptor): # Here I rename attributes name of descriptor`s object.
                value.attr_name = key

    @classmethod
    def __prepare__(metacls, name, bases):
        return OrderedDict()

【问题讨论】:

    标签: metaprogramming python-3.7 metaclass descriptor


    【解决方案1】:

    您“不需要” - 如果您的代码不使用其他自定义元类,则不调用元类'__init__.super() 将同样有效。

    但是,如果需要通过继承将您的元类与另一个元类组合在一起,而没有 super() 调用,它将无法“开箱即用”:super() 调用是确保所有方法在继承链被调用。

    如果一开始看起来元类非常罕见,并且可能永远不会组合元类:一些库或框架有自己的元类,包括 Python 的“abc”(抽象基类)、PyQT、 ORM 框架等等。如果您控制下的任何元类在__new____init____call__ 方法上的正确 super() 调用表现良好(如果您覆盖这些方法),那么您需要做些什么来组合两个超类并使其正常工作元类可以在一行中完成:

    CompatibleMeta = type("CompatibleMeta", (meta, type(OtherClassBase)), {})
    

    这样,例如,如果您想在使用 Python 中的 ABCMeta 功能的类中使用元类中的机制,您只需这样做。 Meta 中的 __init__ 方法将调用另一个元类 __init__。否则它不会运行,并且一些微妙的意外事件不会在你的类中初始化,这可能是一个很难找到的错误。

    附带说明:如果 __prepare__ 所做的只是在高于 3.6 的 Python 上创建 OrderedDict,则无需在元类中声明:从该版本开始,字典在执行时用作“locals()”默认情况下,类主体是有序的。此外,如果您正在组合的另一个元类也有一个__prepare__,则无法使用“super()”自动使其工作 - 您必须检查代码并验证两个 __prepare__s 中的哪一个应该被使用,或者创建一个新的映射类型,它具有参与这两个元类的特性。

    【讨论】:

      猜你喜欢
      • 2013-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-30
      • 1970-01-01
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      相关资源
      最近更新 更多