为什么超级闭包不使用元 class 给出的新基数 class?

Why does super closure not use the new base class given by a metaclass?

我正在尝试创建一个 class,它动态地具有一个基础,该基础是在实例化时传递给它的对象的 class。例如。传递给它一个 int 实例,然后它子 classes int,等等。我试图通过使用 metaclass 来做到这一点。这似乎打破了super__class__ (a closure created if super exists) 和 super 的无参数形式不承认新基数 class.

由于 __class__ / super() 损坏,我不确定如何做 super() 会做的事情。 super(self.__class__, self)“有效”,但会破坏 subclassing this class.

为什么在这种情况下 __class__ 不匹配 self.__class__?我知道它们不是等同的概念,但在这里,我希望它们具有相同的值。请参阅下面的工厂功能替代方案。 print(isinstance(self, Class)) 也 returns 错误。我的猜测是 在解析代码时,最初设置它时没有考虑 metaclass 的变化,并采用了用 type 创建的新 class不会更改已经设置的 __class__ - 尽管我猜它会。毕竟,您正在提供新的 class,不是吗?

__class__/super()可以“固定”吗?如果不能,super 还能以某种方式干净地使用吗?

我尝试创建一个 class 来做我想 super 做的事情,使用 __getattribute__,但这也很冒险。

查看这个带有说明性打​​印的可运行示例:

class Base:
    pass

class Meta(type):
    def __call__(mcls, obj, *args, **kwargs):
        bases = (obj.__class__,)

        __dict__ = dict(mcls.__dict__)
        if "__dict__" in __dict__:
            del __dict__['__dict__']
        if "__weakref__" in __dict__:
            del __dict__['__weakref__']

        cls = type(mcls.__name__, bases , __dict__)
        instance = cls.__new__(cls, *args, **kwargs)
        instance.__init__(*args, **kwargs)
        return instance

class Class(metaclass=Meta):
    def __init__(self, *args, **kwargs):
        print(__class__.__bases__)
        print(self.__class__.__bases__)
        print(isinstance(self, Class))
        print(isinstance(Class, Meta))
        print(isinstance(self, __class__))
        print(isinstance(self, self.__class__))

        super().__init__(*args, **kwargs)
In [1]: Class(Base())
(<class 'object'>,)
(<class '__main__.Base'>,)
False
True
False
True
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-55-6a5013764b7e> in <module>
----> 1 c = Class(Base())

<ipython-input-54-f1865febd768> in __call__(mcls, obj, *args, **kwargs)
     19         instance = cls.__new__(cls, *args, **kwargs)
     20         # Sets obj in instance for mutables
---> 21         instance.__init__(*args, **kwargs)
     22         return instance
     23

<ipython-input-54-f1865febd768> in __init__(self, *args, **kwargs)
     30         print(isinstance(self, self.__class__))
     31
---> 32         super().__init__(*args, **kwargs)
     33

TypeError: super(type, obj): obj must be an instance or subtype of type

工厂函数

我知道上面的内容可以简单地用一个看起来像这样的工厂函数来完成:

class Base:
    def __init__(self, *args, **kwargs):
        print("Base init")

def get_instance(obj):
    class Class(type(obj)):
        def __init__(self, *args, **kwargs):
            print(__class__.__bases__)
            print(self.__class__.__bases__)
            super().__init__(*args, **kwargs)
    return Class()

产生

In [1]: get_instance(Base())
Base init
(<class '__main__.Base'>,)
(<class '__main__.Base'>,)
Base init

在这个例子中,super() 找到了 Base class 因为它是用它定义的,并且直观地找到了 Base 的 init dunder 方法。改述我的问题:

如果我改为使用 metaclass 来动态地子class 某些东西,我怎样才能让 super() 找到动态基础,就像我使用工厂功能?如果不能(干净利落地),是否有合理的替代方案?

另一个措辞

最后,我认为可以重述这个问题的另一种方式是:

如何使用 Metaclass 有效地模拟正常的子classing?

为什么?

首先,这种行为让我很吃惊,我想了解一下。在这方面,这可以被视为一个学术问题。

除此之外,如何动态子类 isn't new 的问题显然不是零兴趣。但是,我所看到的方法似乎都缺乏。 super / __class__ 对于 metaclasses 来说似乎有问题,如果那个和其他一切都工作得很好(如果是的话),仍然有人担心 metaclass 可能存在的冲突一般情况下不用担心。使用工厂函数可能看起来很简单,但它们很难将您创建的class 子class。上面工厂函数示例中的 subclass the Class 并不是直接的。使用元class,如果你能让它工作,仍然允许更简单的访问和子classing。

这里的问题是你的动态 class 根本没有继承自它自己 (Class) - 你的 __init__ 中的隐式 __class__ 变量指向“硬编码”“Class”版本 - 但调用 __init__ 时收到的 self 是动态 class 的一个实例,它没有 Class 作为它超级class。因此,隐式填充到 super() 的参数:__class__self 将不匹配(self 不是 __class__ 中定义的 class 的实例或 sub class 个)。

解决这个问题的可靠方法是允许适当的继承,忘记复制 class __dict__ 属性:让继承机制以适当的顺序调用方法。

通过简单地使动态 class 继承静态 class 和 dynamic-base,所有方法都已到位,并且 self 将是来自的适当实例来自 __init__.__class__ 的 baked-in __class__ 仍然指向静态 Class,但是调用 super 的条件已经满足 - 并且 super 做对了事情,通过寻找从 self 参数开始的超级方法——新创建的动态子 class 继承了静态 Class 和新的 Base,并调用Base 上的方法,因为它们是新 class 的 __mro__ 的一部分,位于正确的位置。

好的 - 听起来很复杂 - 但是添加了一些打印语句我们可以看到它在工作:


class Base:
    def __init__(self):
        print("at base __init__")

class Meta(type):
    def __call__(cls, obj, *args, **kwargs):
        dynamic_ancestor = type(obj)
        bases = (cls, dynamic_ancestor,)

        new_cls = type(f"{cls.__name__}__{dynamic_ancestor.__name__}", bases , {})
        instance = new_cls.__new__(new_cls, *args, **kwargs)
        instance.__init__(*args, **kwargs)
        return instance

class Class(metaclass=Meta):
    def __init__(self, *args, **kwargs):
        print(__class__.__bases__)
        print(self.__class__.__bases__)
        print(isinstance(self, Class))
        print(isinstance(Class, Meta))
        print(isinstance(self, __class__))
        print(isinstance(self, self.__class__))
        print(self.__class__.__mro__, __class__.__mro__)
        super().__init__(*args, **kwargs)

Class(Base())

输出:

at base __init__
(<class 'object'>,)
(<class '__main__.Class'>, <class '__main__.Base'>)
True
True
True
True
(<class '__main__.Class__Base'>, <class '__main__.Class'>, <class '__main__.Base'>, <class 'object'>) (<class '__main__.Class'>, <class 'object'>)
at base __init__