gpt4 book ai didi

Python 多重继承元类错误

转载 作者:行者123 更新时间:2023-12-04 01:19:05 28 4
gpt4 key购买 nike

我正在尝试继承 from ABCctypes.Structure 但我遇到了多重继承和元类错误。

我做错了什么?

我的代码如下:

from ctypes import *
from abc import ABC, abstractmethod, ABCMeta

class FinalMeta(ABCMeta, type(Structure)):
def __new__(mcs, name, bases, namespace, **kwargs):
print("FinalMeta.__new__(mcs, name, bases, namespace, **kwargs)")
print(" mcs =", mcs)
print(" name =", name)
print(" bases =", bases)
print(" namespace =", namespace)
print(" kwargs =", kwargs)
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
print("<-- cls =", cls)
print()
return cls


class MessageBase(ABC, Structure, metaclass=FinalMeta):
def __init__(self, *args, **kwargs):
super().__init__()

@property
@abstractmethod
def format(self):
pass

错误:

File "D:\Development\messages.py", line 13, in __new__
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
File "C:\Users\AppData\Local\Programs\Python\Python37-32\lib\abc.py", line 126, in __new__
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
TypeError: _ctypes.PyCStructType.__new__(FinalMeta) is not safe, use type.__new__()**

最佳答案

这行不通。 yAzou 的回答可能看起来行得通,但实际上行不通 - 它跳过了关键的初始化并使 MessageBase 处于损坏状态,最明显的症状是即使您填写了 _fields_ 并实现了抽象属性在一个具体的子类中,you still won't be able to create instances .

尝试对用 C 编写的类型进行多重继承并没有得到很好的支持。部分行为可能是 Python 错误,但我怀疑它是否会正常工作。这是正在发生的事情。


_ctypes.PyCStructTypectypes.Structure 的元类。这个元类是用 C 语言编写的,这意味着它的 __new__ 是作为其 C 级 tp_new 的包装器生成的插槽。

tp_new 包装器希望确保您永远不会通过执行诸如 object.__new__(tuple) (或更糟的是 tuple .__new__(dict)),因为这可能会导致内存损坏。因此,它有 the following safety check :

static PyObject *
tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds)
{
...
/* Check that the use doesn't do something silly and unsafe like
object.__new__(dict). To do this, we check that the
most derived base that's not a heap type is this type. */
staticbase = subtype;
while (staticbase && (staticbase->tp_new == slot_tp_new))
staticbase = staticbase->tp_base;
/* If staticbase is NULL now, it is a really weird type.
In the spirit of backwards compatibility (?), just shut up. */
if (staticbase && staticbase->tp_new != type->tp_new) {
PyErr_Format(PyExc_TypeError,
"%s.__new__(%s) is not safe, use %s.__new__()",
type->tp_name,
subtype->tp_name,
staticbase->tp_name);
return NULL;
}

这试图确保当您使用用 C 编写的 ClassA 调用 ClassA.__new__(ClassB, ...) 时,ClassAClassB 继承层次结构最下方的 C 类。但是,它通过 tp_base 指针遵循 单个 基类链,忽略多重继承!

随着 tp_base 指针的初始化方式(参见 Objects/typeobject.c 中的 best_base),跟随 tp_base 指针通常会导致到最派生的 C 类,但这取决于每个 C 类向其(单个)基类的实例内存布局添加字段。 _ctypes.PyCStructType 不这样做(它用一个奇怪的自定义 dict 子类替换类 dict 来存储它的数据),所以 FinalMeta 的 tp_base 链变成了 FinalMeta->ABCMeta->type->object.

由于 _ctypes.PyCStructType 不在 tp_base 链中,tp_new_wrapper 认为 type 是最派生的FinalMeta 的 C 祖先,它认为您应该改为调用 type.__new__。但是,如果您调用 type.__new__,您将跳过初始化,此检查本应阻止您跳过,从而使您的类处于中断状态。


您可能认为可以通过重新排列基数以将 type(Structure) 放在 ABCMeta 之前来解决这个问题,但这只解决了一半问题。您将能够定义您的 MessageBase 类,但是 ABCMeta.__new__ 不会运行,并且它不会执行标记您的 MessageBase 所需的初始化 摘要。

另外,还有一个完全不相关的长期存在的问题,即使 MessageBase 被标记为抽象的,您仍然能够创建它的实例。防止实例化抽象类的检查在 object.__new__ 中,以及 object 以外的 C 类(以及此类类的后代)不调用 object.__new__

关于Python 多重继承元类错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62861620/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com