Python C-API에서 파생 된 형식을 동적으로 만드는 방법
Python 용 C 확장 모듈 작성Noddy 에 대한 자습서에 정의 된 유형이 있다고 가정합니다 . 이제 파생 된 형식을 만들고의 __new__()메서드 만 덮어 쓰려고합니다 Noddy.
현재 다음 접근 방식을 사용합니다 (가독성을 위해 제거 된 오류 검사).
PyTypeObject *BrownNoddyType =
(PyTypeObject *)PyType_Type.tp_alloc(&PyType_Type, 0);
BrownNoddyType->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
BrownNoddyType->tp_name = "noddy.BrownNoddy";
BrownNoddyType->tp_doc = "BrownNoddy objects";
BrownNoddyType->tp_base = &NoddyType;
BrownNoddyType->tp_new = BrownNoddy_new;
PyType_Ready(BrownNoddyType);
이것은 작동하지만 그것이 올바른 방법인지 잘 모르겠습니다. Py_TPFLAGS_HEAPTYPE힙에 유형 객체를 동적으로 할당하기 때문에 플래그도 설정해야한다고 예상 했지만 그렇게하면 인터프리터에서 segfault가 발생합니다.
또한 type()using PyObject_Call()또는 이와 유사한 것을 명시 적으로 호출 하는 것에 대해 생각 했지만 아이디어를 버렸습니다. 함수 BrownNoddy_new()를 Python 함수 개체 로 래핑 __new__하고이 함수 개체에 대한 사전 매핑 을 만들어야합니다 . 어리석은 것 같습니다.
이것에 대해 가장 좋은 방법은 무엇입니까? 내 접근 방식이 맞습니까? 내가 놓친 인터페이스 기능이 있습니까?
최신 정보
python-dev 메일 링리스트 (1) (2) 의 관련 주제에 대한 두 개의 스레드가 있습니다 . 이 스레드와 몇 가지 실험을 Py_TPFLAGS_HEAPTYPE통해 유형이에 대한 호출에 의해 할당되지 않는 한 설정해서는 안된다고 추론합니다 type(). 유형을 수동으로 할당하거나을 호출하는 것이 더 나은지 여부에 관계없이 이러한 스레드에는 다른 권장 사항이 있습니다 type(). tp_new슬롯 에 들어가야하는 C 함수를 래핑하는 권장 방법이 무엇인지 알기 만하면 후자에 만족할 것 입니다. 일반 메서드의 경우이 단계는 쉬울 것 PyDescr_NewMethod()입니다. 적절한 래퍼 개체를 가져 오는 데 사용할 수 있습니다 . __new__()그래도 내 메서드에 대해 이러한 래퍼 개체를 만드는 방법을 모르겠습니다. 이러한 래퍼 개체 PyCFunction_New()를 만들려면 문서화되지 않은 함수 가 필요할 수 있습니다.
Python 3과 호환되도록 확장을 수정할 때 동일한 문제가 발생했으며이를 해결하려고 할 때이 페이지를 발견했습니다.
결국 파이썬 인터프리터의 소스 코드 인 PEP 0384 와 C-API에 대한 문서를 읽음으로써이 문제를 해결했습니다 .
설정 Py_TPFLAGS_HEAPTYPE플래그하면 개주하는 인터프리터 이야기 PyTypeObject로 PyHeapTypeObject도 할당해야하는 추가 멤버를 포함. 어떤 시점에서 통역사는 이러한 추가 구성원을 참조하려고 시도하며 할당되지 않은 상태로두면 세그 폴트가 발생합니다.
Python 3.2 는 동적 유형 생성을 단순화 하는 C 구조 PyType_Slot와 PyType_SpecC 함수 PyType_FromSpec를 도입했습니다 . 요컨대, PyType_Slot및 PyType_Spec을 사용하여의 tp_*구성원 을 지정한 PyTypeObject다음 PyType_FromSpec메모리 할당 및 초기화의 더러운 작업을 수행하기 위해 호출 합니다.
PEP 0384부터는 다음이 있습니다.
typedef struct{
int slot; /* slot id, see below */
void *pfunc; /* function pointer */
} PyType_Slot;
typedef struct{
const char* name;
int basicsize;
int itemsize;
int flags;
PyType_Slot *slots; /* terminated by slot==0. */
} PyType_Spec;
PyObject* PyType_FromSpec(PyType_Spec*);
(위의 내용은 PEP 0384의 리터럴 사본이 아닙니다.이 사본은 const char *doc의 멤버로 도 포함 됩니다 PyType_Spec. 그러나 해당 멤버는 소스 코드에 나타나지 않습니다.)
원래 예제에서이를 사용하려면 BrownNoddy기본 클래스에 대한 C 구조를 확장하는 C 구조 가 있다고 가정 Noddy합니다. 그러면 우리는
PyType_Slot slots[] = {
{ Py_tp_doc, "BrownNoddy objects" },
{ Py_tp_base, &NoddyType },
{ Py_tp_new, BrownNoddy_new },
{ 0 },
};
PyType_Spec spec = { "noddy.BrownNoddy", sizeof(BrownNoddy), 0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots };
PyTypeObject *BrownNoddyType = (PyTypeObject *)PyType_FromSpec(&spec);
이것은 호출을 포함하여 원래 코드에서 모든 작업을 수행해야 하며 .NET Framework에 대한 추가 메모리 할당 및 초기화를 PyType_Ready포함하여 동적 유형을 만드는 데 필요한 작업을 수행해야 Py_TPFLAGS_HEAPTYPE합니다 PyHeapTypeObject.
도움이 되었기를 바랍니다.
이 답변이 끔찍한 경우 미리 사과하지만 PythonQt 에서이 아이디어의 구현을 찾을 수 있습니다 . 특히 다음 파일이 유용한 참조가 될 수 있다고 생각합니다.
PythonQtClassWrapper_init의이 조각은 나에게 다소 흥미로운 것으로 튀어 나온다.
static int PythonQtClassWrapper_init(PythonQtClassWrapper* self, PyObject* args, PyObject* kwds)
{
// call the default type init
if (PyType_Type.tp_init((PyObject *)self, args, kwds) < 0) {
return -1;
}
// if we have no CPP class information, try our base class
if (!self->classInfo()) {
PyTypeObject* superType = ((PyTypeObject *)self)->tp_base;
if (!superType || (superType->ob_type != &PythonQtClassWrapper_Type)) {
PyErr_Format(PyExc_TypeError, "type %s is not derived from PythonQtClassWrapper", ((PyTypeObject*)self)->tp_name);
return -1;
}
// take the class info from the superType
self->_classInfo = ((PythonQtClassWrapper*)superType)->classInfo();
}
return 0;
}
It's worth noting that PythonQt does use a wrapper generator, so it's not exactly in line with what you're asking for, but personally I think trying to outsmart the vtable isn't the most optimal design. Basically, there are many different C++ wrapper generators for Python and people use them for a good reason - they're documented, there are examples floating around in search results and on stack overflow. If you hand roll a solution for this that nobody's seen before, it'll be that much harder for them to debug if they run into problems. Even if it's closed-source, the next guy who has to maintain it will be scratching his head and you'll have to explain it to every new person who comes along.
Once you get a code generator working, all you need to do is maintain the underlying C++ code, you don't have to update or modify your extension code by hand. (Which is probably not too far away from the tempting solution you went with)
The proposed solution is an example of breaking the type-safety that the newly introduced PyCapsule provides a bit more protection against (when used as directed).
So, while its possible it might not be the best long term choice to implement derived/subclasses this way, but rather wrap the code and let the vtable do what it does best and when the new guy has questions you can just point him at the documentation for whatever solution fits best.
This is just my opinion though. :D
One way to try and understand how to do this is to create a version of it using SWIG. See what it produces and see if it matches or is done a different way. From what I can tell the people who have been writing SWIG have an in depth understanding of extending Python. Can't hurt to see how they do things at any rate. It may help you understand this problem.
'Program Club' 카테고리의 다른 글
| TemplateHaskell을 사용하여 네임 스페이스의 모든 이름 나열 (0) | 2020.10.27 |
|---|---|
| Python 3.x에는 어떤 SOAP 라이브러리가 있습니까? (0) | 2020.10.27 |
| 내 모든 JavaFX TextField에는 줄이 있습니다. (0) | 2020.10.27 |
| PLBuildVersion 클래스는 둘 다 / 응용 프로그램에서 구현됩니다. (0) | 2020.10.27 |
| 이 PHP / MySQL 뉴스 피드를 어떻게 개선 할 수 있습니까? (0) | 2020.10.27 |