这有点令人费解,所以请耐心等待。在_ctypes.c(3.8.5版)中,第5788行有:
Py_TYPE(&Simple_Type) = &PyCSimpleType_Type;
Simple_Type.tp_base = &PyCData_Type;
if (PyType_Ready(&Simple_Type) < 0)
return NULL;
Py_INCREF(&Simple_Type);
PyModule_AddObject(m, "_SimpleCData", (PyObject *)&Simple_Type);
这会将Simple_Type 的地址公开为名称_SimpleCData。 Simple_Type(也就是 Python 世界中的 _SimpleCData)也继承自 PyCDataType(稍后会详细介绍)。一、Simple_Type的定义在5040行:
static PyTypeObject Simple_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ctypes._SimpleCData",
sizeof(CDataObject), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
(reprfunc)&Simple_repr, /* tp_repr */
&Simple_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
&PyCData_as_buffer, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"XXX to be provided", /* tp_doc */
(traverseproc)PyCData_traverse, /* tp_traverse */
(inquiry)PyCData_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Simple_methods, /* tp_methods */
0, /* tp_members */
Simple_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Simple_init, /* tp_init */
0, /* tp_alloc */
GenericPyCData_new, /* tp_new */
0, /* tp_free */
};
这本质上是一个用于定义新类的元类。此结构的成员之一(带有注释 /* tp_methods */)引用了第 4996 行定义的以下数组:
static PyMethodDef Simple_methods[] = {
{ "__ctypes_from_outparam__", Simple_from_outparm, METH_NOARGS, },
{ NULL, NULL },
这应该是由该元类创建的类将具有的方法列表。可是等等!那只是一种方法。让我们看看它继承自的类,PyCSimpleType_Type 在第 2327 行:
PyTypeObject PyCSimpleType_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ctypes.PyCSimpleType", /* tp_name */
0, /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
&CDataType_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"metatype for the PyCSimpleType Objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
PyCSimpleType_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
PyCSimpleType_new, /* tp_new */
0, /* tp_free */
};
它带来的方法是PyCSimpleType_methods在第2318行定义的:
static PyMethodDef PyCSimpleType_methods[] = {
{ "from_param", PyCSimpleType_from_param, METH_O, from_param_doc },
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
{ "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
{ "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc},
{ NULL, NULL },
};
因此,元类似乎构建了一个新类,包括元类的 struct 定义及其基类的tp_methods 成员指定的所有方法。 这应该导致from_buffer 方法对于元类创建的所有类的所有对象具有相同的地址。
参考您的帖子:
>>> ctypes.c_long.from_buffer
<built-in method from_buffer of _ctypes.PyCSimpleType object at 0x16f8fb0>
>>> ctypes._SimpleCData.from_buffer
<built-in method from_buffer of _ctypes.PyCSimpleType object at 0x7f19f9b09ae0>
我现在认为您误解了输出的内容。 0x16f8fb0 和 0x7f19f9b09ae0 不是 from_buffer 方法的地址,这两个对象应该相同,而是实现这些类型的 ctypes.PyCSimpleType 对象本身的地址。
Mark Tolonen 说得对,但如果你需要说服力(我也这样做了)...