확장 모듈 격리하기¶
독자 대상¶
이 가이드는 파이썬 자체가 라이브러리로 사용되는 애플리케이션에서 해당 확장을 더 안전하게 사용할 수 있도록 만들고자 하는 C-API 확장 모듈 유지관리자를 위해 작성되었습니다.
배경¶
인터프리터 는 파이썬 코드가 실행되는 환경입니다. 여기에는 설정(예: 가져오기 경로)과 런타임 상태(예: 임포트된 모듈 세트)가 포함됩니다.
파이썬은 하나의 프로세스에서 여러 개의 인터프리터를 실행하는 것을 지원합니다. 고려해야 할 두 가지 경우가 있습니다. 사용자는 다음과 같이 인터프리터를 실행할 수 있습니다.
연속적으로 여러 번의
Py_InitializeEx()/Py_FinalizeEx()사이클을 거치거나,병렬로 ; 즉,
Py_NewInterpreter()/Py_EndInterpreter()를 사용하여 “서브 인터프리터”를 관리하거나.
두 경우(및 이들의 조합)는 라이브러리 내에 파이썬을 임베딩할 때 가장 유용합니다. 일반적으로 라이브러리는 이를 사용하는 애플리케이션에 대해 어떠한 가정도 해서는 안 되며, 여기에는 프로세스 전체에 걸친 “메인 파이썬 인터프리터”가 존재한다고 가정하는 것도 포함됩니다.
역사적으로 파이썬 확장 모듈은 이러한 활용 사례를 잘 처리하지 못해 왔습니다. 많은 확장 모듈(그리고 일부 표준 라이브러리 모듈까지도)은 C의 static 변수를 사용하기 매우 편리하기 때문에 프로세스별 전역 상태를 사용합니다. 이로 인해 특정 인터프리터에만 해당해야 하는 데이터가 여러 인터프리터 간에 공유되는 결과가 초래됩니다. 확장 모듈 개발자가 주의를 기울이지 않으면, 동일한 프로세스 내의 두 개 이상의 인터프리터에서 모듈을 로드할 때 충돌을 일으키는 예외 상황이 발생하기 매우 쉽습니다.
안타깝게도 인터프리터별 상태를 구현하는 것은 쉽지 않습니다. 확장 모듈 작성자들은 개발 시 여러 인터프리터를 고려하지 않는 경향이 있으며, 현재로서는 이러한 동작을 테스트하는 것이 번거롭습니다.
모듈별 상태 도입¶
파이썬의 C API는 인터프리터별 상태에 집중하는 대신, 더 세분화된 모듈별 상태를 더 잘 지원하도록 발전하고 있습니다. 이는 C 수준의 데이터를 모듈 객체 에 연결해야 함을 의미합니다. 각 인터프리터는 고유한 모듈 객체를 생성하여 데이터를 분리하여 유지합니다. 격리를 테스트하기 위해, 하나의 확장 프로그램에 해당하는 여러 모듈 객체를 단일 인터프리터 내에 로드할 수도 있습니다.
모듈별 상태는 수명과 리소스 소유권을 생각하는 쉬운 방법을 제공합니다. 확장 모듈은 모듈 객체가 생성될 때 초기화되고, 해제될 때 정리됩니다. 이런 관점에서 모듈은 다른 어떤 PyObject* 와도 마찬가지이며, “인터프리터 종료 시” 실행되는 훅을 고려하거나 잊어버릴 필요가 없습니다.
프로세스별, 인터프리터별, 스레드별 또는 작업(task)별 상태와 같은 다양한 종류의 “전역” 상태가 필요할 수도 있음을 유의하십시오. 모듈별 상태를 기본으로 사용하더라도 이러한 것들이 가능은 하지만, 예외적인 경우로 취급해야 합니다. 만약 이러한 상태가 필요한 경우, 추가적인 주의와 테스트가 필요합니다. (이 가이드에서는 이를 다루지 않습니다.)
격리된 모듈 객체¶
확장 모듈을 개발할 때 명심해야 할 핵심 사항은 단일 공유 라이브러리로부터 여러 개의 모듈 객체가 생성될 수 있다는 점입니다. 예를 들어:
>>> import sys
>>> import binascii
>>> old_binascii = binascii
>>> del sys.modules['binascii']
>>> import binсяccii # 새 모듈 객체 생성
>>> old_binascii == binascii
False
일반적인 원칙으로, 두 모듈은 완전히 독립적이어야 합니다. 모듈에 특화된 모든 객체와 상태는 모듈 객체 내에 캡슐화되어야 하며, 다른 모듈 객체와 공유되어서는 안 되고, 모듈 객체가 해제될 때 정리되어야 합니다. 이는 일반적인 원칙이므로 예외가 발생할 수 있으나(Managing Global State 참조), 이 경우 더 많은 고민과 예외 상황에 대한 주의가 필요합니다.
일부 모듈은 덜 엄격한 제한으로도 충분할 수 있지만, 격리된 모듈을 사용하면 다양한 활용 사례에 걸쳐 작동하는 명확한 기대치와 가이드라인을 설정하기가 더 쉬워집니다.
의외의 예외 상황¶
격리된 모듈은 일부 놀라운 엣지 케이스를 생성한다는 점에 유념하십시오. 가장 주목할 만한 점은, 각 모듈 객체가 일반적으로 다른 유사한 모듈과 클래스 및 예외를 공유하지 않는다는 것입니다. 위의 example above 를 계속하면, old_binascii.Error 와 binascii.Error 가 별개의 객체라는 점에 유념하십시오. 다음 코드에서는 예외가 포착되지 않습니다:
>>> old_binascii.Error == binascii.Error
False
>>> try:
... old_binascii.unhexlify(b'qwertyuiop')
... except binascii.Error:
... print('boo')
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
binascii.Error: Non-hexadecimal digit found
이는 예상된 결과입니다. 순수 파이썬 모듈도 동일하게 동작한다는 점에 주목하십시오. 이것은 파이썬의 작동 방식 중 일부입니다.
목표는 확장 모듈을 C 수준에서 안전하게 만드는 것이지, 편법(hack)이 직관적으로 작동하게 만드는 것이 아닙니다. sys.modules 를 “수동으로” 수정하는 것은 편법에 해당합니다.
다중 인터프리터 환경에서 모듈을 안전하게 만들기¶
전역 상태 관리¶
때때로 파이썬 모듈과 관련된 상태가 해당 모듈에 특화된 것이 아니라 프로세스 전체(또는 모듈보다 “더 전역적인” 무언가)와 관련된 경우가 있습니다. 예를 들어:
readline모듈은 그 터미널을 관리합니다.회로 기판에서 실행되는 모듈은 그 온보드 LED를 제어하려고 합니다.
이러한 경우, 파이썬 모듈은 전역 상태를 소유 하는 대신 해당 상태에 대한 접근 을 제공해야 합니다. 가능하다면, 여러 복사본이 독립적으로 상태에 접근할 수 있도록(파이썬이나 다른 언어를 위한 다른 라이브러리와 함께) 모듈을 작성하십시오. 그것이 불가능하다면 명시적인 잠금(locking)을 고려하십시오.
프로세스 전역 상태를 사용해야 하는 경우, 여러 인터프리터와 관련된 문제를 방지하는 가장 간단한 방법은 모듈이 프로세스당 한 번 이상 로드되지 않도록 명시적으로 차단하는 것입니다. 옵트아웃: 프로세스당 하나의 모듈 객체로 제한하기 을 참조하십시오.
모듈별 상태 관리¶
모듈별 상태를 사용하려면 multi-phase extension module initialization 을 사용하십시오. 이는 해당 모듈이 여러 인터프리터를 올바르게 지원함을 나타냅니다.
PyModuleDef.m_size 를 양의 숫자로 설정하여 모듈 전용 저장 공간을 요청하십시오. 일반적으로 이는 모든 모듈 수준의 C 상태를 저장할 수 있는 특정 모듈의 struct 크기로 설정됩니다. 특히, 여기에는 C 코드가 작동하는 데 필요한 클래스(예외 포함, 정적 타입 제외) 및 설정(예: csv 의 field_size_limit)에 대한 포인터를 두어야 합니다.
참고
또 다른 옵션은 모듈의 __dict__ 에 상태를 저장하는 것이지만, 사용자가 파이썬 코드에서 __dict__ 를 수정할 때 충돌이 발생하지 않도록 해야 합니다. 이는 보통 C 수준에서의 오류 및 유형 검사를 의미하며, 이는 잘못되기 쉽고 충분히 테스트하기 어렵습니다.
그러나 모듈 상태가 C 코드에서 필요하지 않다면, __dict__ 에만 저장하는 것이 좋은 방법입니다.
모듈 상태에 PyObject 포인터가 포함된 경우, 모듈 객체는 해당 객체들에 대한 참조를 보유해야 하며 모듈 수준의 훅인 m_traverse, m_clear 및 m_free 를 구현해야 합니다. 이들은 클래스의 tp_traverse, tp_clear, tp_free 와 유사하게 작동합니다. 이를 추가하려면 작업이 필요하고 코드가 길어지겠지만, 이는 모듈을 깔끔하게 언로드할 수 있게 하는 대가입니다.
An example of a module with per-module state is currently available as xxlimited; example module initialization shown at the bottom of the file.
옵트아웃: 프로세스당 하나의 모듈 객체로 제한하기¶
0 이상의 PyModuleDef.m_size 는 모듈이 여러 인터프리터를 올바르게 지원함을 나타냅니다. 아직 그러한 경우가 아니라면, 모듈을 프로세스당 한 번만 로드 가능하도록 명시적으로 설정할 수 있습니다. 예:
// 프로세스 전체에 적용되는 플래그
static int loaded = 0;
// 스레드 안전성을 제공하기 위한 뮤텍스 (free-threaded Python에서만 필요)
static PyMutex modinit_mutex = {0};
static int
exec_module(PyObject* module)
{
PyMutex_Lock(&modinit_mutex);
if (loaded) {
PyMutex_Unlock(&modinit_mutex);
PyErr_SetString(PyExc_ImportError,
"cannot load module more than once per process");
return -1;
}
loaded = 1;
PyMutex_Unlock(&modinit_mutex);
// ... 나머지 초기화
}
모듈의 PyModuleDef.m_clear 함수가 향후 재초기화를 대비할 수 있는 경우, loaded 플래그를 해제해야 합니다. 이 경우 모듈이 여러 인스턴스가 동시에 존재하는 것은 지원하지 않지만, 예를 들어 파이썬 런타임 종료(Py_FinalizeEx()) 이후에 로드되거나 재초기화(Py_Initialize())되는 경우는 지원하게 됩니다.
함수에서 모듈 상태 접근하기¶
모듈 수준의 함수에서 상태에 접근하는 것은 간단합니다. 함수는 첫 번째 인자로 모듈 객체를 받으며, 상태를 추출하려면 PyModule_GetState 를 사용할 수 있습니다.
static PyObject *
func(PyObject *module, PyObject *args)
{
my_struct *state = (my_struct*)PyModule_GetState(module);
if (state == NULL) {
return NULL;
}
// ... 나머지 로직
}
참고
모듈 상태가 없는 경우(즉, PyModuleDef.m_size 가 0인 경우), PyModule_GetState 는 예외를 설정하지 않고 NULL 을 반환할 수 있습니다. 자체 모듈에서는 m_size 를 제어하므로 이를 방지하기 쉽습니다.
힙 형¶
전통적으로 C 코드에서 정의된 형은 정적(static) 입니다. 즉, 코드 내에서 직접 정의되고 PyType_Ready() 를 사용하여 초기화되는 static PyTypeObject 구조체입니다.
이러한 형은 필연적으로 프로세스 전체에서 공유됩니다. 이를 모듈 객체 간에 공유할 때는 해당 형이 소유하거나 접근하는 상태에 주의를 기울여야 합니다. 발생 가능한 문제를 제한하기 위해, 정적형은 파이썬 수준에서 불변(immutable)입니다. 예를 들어 str.myattribute = 123 과 같이 설정할 수 없습니다.
가변 객체에 대한 접근을 제공하지 않는 한, 진정으로 불변인 객체를 인터프리터 간에 공유하는 것은 괜찮습니다. 그러나 CPython에서 모든 파이썬 객체는 가변적인 구현 세부 사항인 참조 횟수(reference count)를 가지고 있습니다. 참조 횟수의 변경은 GIL에 의해 보호됩니다. 따라서 어떤 파이썬 객체든 인터프리터 간에 공유하는 코드는 명시적으로나 암묵적으로나 CPython의 현재 프로세스 범위의 GIL에 의존하게 됩니다.
정적형은 불변이며 프로세스 전역에 해당하므로, 자신들의 모듈 상태에 접근할 수 없습니다. 이러한 형의 메서드 중 어느 하나라도 모듈 상태에 대한 접근이 필요한 경우, 해당 형을 힙 할당 형*(줄여서 **heap type*)으로 변환해야 합니다. 이는 파이썬의 class 문으로 생성된 클래스와 더 밀접하게 대응됩니다.
새로운 모듈의 경우 기본적으로 heap 형을 사용하는 것이 좋은 원칙입니다.
정적 형(Static Type)을 힙 형(Heap Type)으로 변경하기¶
정적 형은 힙 형으로 변환할 수 있지만, 힙 형 API가 정적 형으로부터 ‘무손실’ 변환(즉, 특정 정적 형과 정확히 동일하게 작동하는 형을 생성하는 것)을 위해 설계되지 않았다는 점에 유의하십시오. 따라서 새로운 API에서 클래스 정의를 다시 작성할 때 몇 가지 세부 사항(예: pickle 가능성 또는 상속된 슬롯 등)이 의도치 않게 변경될 수 있습니다. 항상 중요한 세부 사항을 테스트하십시오.
특히 다음 두 가지 사항에 주의하십시오(단, 이것이 모든 항목을 포함하는 것은 아닙니다):
정적 형과 달리 힙 형 객체는 기본적으로 가변(mutable)입니다. 가변성을 방지하려면
Py_TPFLAGS_IMMUTABLETYPE플래그를 사용하십시오.힙 형은 기본적으로
tp_new을 상속하므로 파이썬 코드에서 인스턴스화가 가능할 수 있습니다.Py_TPFLAGS_DISALLOW_INSTANTIATION플래그를 사용하여 이를 방지할 수 있습니다.
힙 형 정의하기¶
힙 형은 클래스의 설명 또는 “청사진”인 PyType_Spec 구조체를 채우고, PyType_FromModuleAndSpec() 를 호출하여 새로운 클래스 객체를 생성함으로써 만들 수 있습니다.
참고
PyType_FromSpec() 과 같은 다른 함수들도 힙 형을 생성할 수 있지만, PyType_FromModuleAndSpec() 은 모듈을 클래스와 연결하여 메서드에서 모듈 상태에 접근할 수 있게 합니다.
클래스는 일반적으로 C에서 안전하게 접근할 수 있도록 모듈 상태(module state)와 파이썬 코드에서 접근할 수 있도록 모듈의 __dict__ 양쪽 모두에 저장되어야 합니다.
가비지 컬렉션 프로토콜¶
힙 형의 인스턴스는 해당 형에 대한 참조를 보유합니다. 이는 모든 인스턴스가 파괴되기 전에 형이 파괴되지 않도록 보장하지만, 가비지 컬렉터가 해제해야 하는 참조 순환(reference cycles)을 초래할 수 있습니다.
메모리 누수를 방지하기 위해 힙 형의 인스턴스는 가비지 컬렉션 프로토콜을 구현해야 합니다. 즉, 힙 형은 다음과 같아야 합니다.
Py_TPFLAGS_HAVE_GC플래그를 가져야 합니다.Py_tp_traverse를 사용하여 형을 방문하는(예:Py_VISIT(Py_TYPE(self))사용) 순회(traverse) 함수를 정의해야 합니다.
추가 고려 사항은 Py_TPFLAGS_HAVE_GC 및 tp_traverse 의 문서를 참조하십시오.
힙 형을 정의하기 위한 API는 유기적으로 발전해 왔기 때문에 현재 상태에서는 사용법이 다소 까다로울 수 있습니다. 다음 섹션에서 일반적인 문제들에 대해 안내합니다.
Python 3.8 이하의 tp_traverse¶
tp_traverse 에서 형을 방문해야 하는 요구 사항은 Python 3.9에 추가되었습니다. Python 3.8 이하를 지원하는 경우 순회 함수가 형을 방문해서는 안 되므로 구현이 더 복잡해집니다:
static int my_traverse(PyObject *self, visitproc visit, void *arg)
{
if (Py_Version >= 0x03090000) {
Py_VISIT(Py_TYPE(self));
}
return 0;
}
안타깝게도 Py_Version 은 Python 3.11에서만 추가되었습니다. 대신 다음을 사용하십시오.
안정적인 ABI를 사용하지 않는 경우
PY_VERSION_HEX를 사용하거나,PySys_GetObject()와PyArg_ParseTuple()을 통한sys.version_info를 사용하십시오.
tp_traverse 위임하기¶
순회 함수가 베이스 클래스(또는 다른 형)의 tp_traverse 에 위임하는 경우, Py_TYPE(self) 를 단 한 번만 방문하도록 보장해야 합니다. tp_traverse 에서 형을 방문하는 것은 힙 형에서만 예상됨에 유의하십시오.
예를 들어, 순회 함수가 다음을 포함하는 경우:
base->tp_traverse(self, visit, arg)
…그리고 base 가 정적 형일 수 있으므로 다음을 포함해야 합니다:
if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) {
// 힙 형의 tp_traverse는 이미 Py_TYPE(self)를 방문했습니다
} else {
if (Py_Version >= 0x03090000) {
Py_VISIT(Py_TYPE(self));
}
}
tp_dealloc 정의하기¶
형에 사용자 정의 tp_dealloc 함수가 있는 경우 다음이 필요합니다.
어떠한 필드가 무효화되기 전에
PyObject_GC_UnTrack()을 호출하고,형의 참조 횟수를 감소시켜야 합니다.
tp_free 가 호출되는 동안 형을 유효하게 유지하려면 인스턴스가 해제된 후에 형의 참조 횟수를 감소시켜야 합니다. 예를 들어:
static void my_dealloc(PyObject *self)
{
PyObject_GC_UnTrack(self);
...
PyTypeObject *type = Py_TYPE(self);
type->tp_free(self);
Py_DECREF(type);
}
기본 tp_dealloc 함수가 이 작업을 수행하므로, 형이 tp_dealloc 을 재정의하지 않는다면 추가할 필요가 없습니다.
tp_free 를 재정의하지 않기¶
힙 형의 tp_free 슬롯은 PyObject_GC_Del() 로 설정되어야 합니다. 이것은 기본값이므로 재정의하지 마십시오.
PyObject_New 피하기¶
GC로 추적되는 객체는 GC를 인식하는 함수를 사용하여 할당되어야 합니다.
PyObject_New() 또는 PyObject_NewVar() 를 사용하는 경우:
가능하다면 형의
tp_alloc슬롯을 가져와 호출하십시오. 즉,TYPE *o = PyObject_New(TYPE, typeobj)를 다음으로 대체하십시오:TYPE *o = typeobj->tp_alloc(typeobj, 0);
o = PyObject_NewVar(TYPE, typeobj, size)를 동일한 방식으로 대체하되, 0 대신 size를 사용하십시오.위의 방법이 불가능한 경우(예: 사용자 정의
tp_alloc내부),PyObject_GC_New()또는PyObject_GC_NewVar()를 호출하십시오:TYPE *o = PyObject_GC_New(TYPE, typeobj); TYPE *o = PyObject_GC_NewVar(TYPE, typeobj, size);
클래스에서 모듈 상태에 접근하기¶
PyType_FromModuleAndSpec() 로 정의된 형 객체가 있는 경우, PyType_GetModule() 을 호출하여 연결된 모듈을 가져온 다음 PyModule_GetState() 를 통해 모듈의 상태를 가져올 수 있습니다.
번거로운 오류 처리 상용구 코드를 줄이기 위해 이 두 단계를 PyType_GetModuleState() 로 결합할 수 있으며, 결과는 다음과 같습니다:
my_struct *state = (my_struct*)PyType_GetModuleState(type);
if (state == NULL) {
return NULL;
}
일반 메서드에서 모듈 상태에 접근하기¶
클래스의 메서드에서 모듈 수준의 상태에 접근하는 것은 다소 복잡하지만, Python 3.9에 도입된 API 덕분에 가능합니다. 상태를 가져오려면 먼저 정의하는 클래스(defining class) 를 가져온 다음, 거기에서 모듈 상태를 가져와야 합니다.
가장 큰 장애물은 메서드가 정의된 클래스, 즉 메서드의 “정의하는 클래스(defining class)”를 가져오는 것입니다. 정의하는 클래스는 자신이 속한 모듈에 대한 참조를 가질 수 있습니다.
정의하는 클래스를 Py_TYPE(self) 와 혼동하지 마십시오. 메서드가 여러분의 형의 서브 클래스 에서 호출되는 경우, Py_TYPE(self) 는 해당 서브 클래스를 가리키며, 이는 여러분의 모듈과 다른 모듈에 정의되어 있을 수 있습니다.
참고
다음 파이썬 코드로 이 개념을 설명할 수 있습니다. type(self) == Sub 인 경우에도 Base.get_defining_class 는 Base 를 반환합니다.
class Base:
def get_type_of_self(self):
return type(self)
def get_defining_class(self):
return __class__
class Sub(Base):
pass
메서드가 “정의하는 클래스”를 가져오려면 METH_METHOD | METH_FASTCALL | METH_KEYWORDS calling convention 와 그에 대응하는 PyCMethod 시그니처를 사용해야 합니다:
PyObject *PyCMethod(
PyObject *self, // 메서드가 호출된 객체
PyTypeObject *defining_class, // 정의하는 클래스
PyObject *const *args, // 인자 C 배열
Py_ssize_t nargs, // "args"의 길이
PyObject *kwnames) // NULL 또는 키워드 인자 딕셔닝
정의하는 클래스를 얻은 후, PyType_GetModuleState() 를 호출하여 해당 모듈과 연결된 상태를 가져옵니다.
예를 들면:
static PyObject *
example_method(PyObject *self,
PyTypeObject *defining_class,
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames)
{
my_struct *state = (my_struct*)PyType_GetModuleState(defining_class);
if (state == NULL) {
return NULL;
}
... // 나머지 로직
}
PyDoc_STRVAR(example_method_doc, "...");
static PyMethodDef my_methods[] = {
{"example_method",
(PyCFunction)(void(*)(void))example_method,
METH_METHOD|METH_FASTCALL|METH_KEYWORDS,
example_method_doc}
{NULL},
}
슬롯 메서드, Getter 및 Setter에서 모듈 상태에 접근하기¶
참고
이 기능은 Python 3.11에서 새로 추가되었습니다.
슬롯 메서드(예를 들어, __add__ 에 대응하는 nb_add 또는 초기화에 대응하는 tp_new 와 같은 특별한 메서드의 빠른 C 등가체)는 PyCMethod 와 달리 정의하는 클래스를 전달할 수 없는 매우 간단한 API를 가지고 있습니다. 이는 PyGetSetDef 로 정의된 getter 및 setter도 마찬가지입니다.
이러한 경우 모듈 상태에 접근하려면 PyType_GetModuleByDef() 함수를 사용하여 모듈 정의를 전달하십시오. 모듈을 확보한 후, PyModule_GetState() 를 호출하여 상태를 가져옵니다:
PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &module_def);
my_struct *state = (my_struct*)PyModule_GetState(module);
if (state == NULL) {
return NULL;
}
PyType_GetModuleByDef() 는 method resolution order (즉, 모든 상위 클래스)를 검색하여 대응하는 모듈을 가진 첫 번째 상위 클래스를 찾음으로써 작동합니다.
참고
매우 특이한 경우(동일한 정의로부터 생성된 여러 모듈에 걸쳐 있는 상속 체인), PyType_GetModuleByDef() 가 실제 정의하는 클래스의 모듈을 반환하지 않을 수 있습니다. 그러나 이 함수는 항상 동일한 정의를 가진 모듈을 반환하므로 호환 가능한 C 메모리 레이아웃을 보장합니다.
모듈 상태의 수명¶
모듈 객체가 가비지 컬렉션될 때 해당 모듈 상태가 해제됩니다. 모듈 상태의 (일부)를 가리키는 각 포인터에 대해 모듈 객체에 대한 참조를 유지해야 합니다.
PyType_FromModuleAndSpec() 로 생성된 형과 그 인스턴스는 모듈에 대한 참조를 보유하므로 일반적으로는 문제가 되지 않습니다. 하지만 외부 라이브러리의 콜백과 같이 다른 위치에서 모듈 상태를 참조할 때는 참조 횟수 계산 시 주의해야 합니다.
미해결 이슈¶
모듈별 상태 및 힙 형과 관련된 몇 가지 문제가 여전히 남아 있습니다.
상황 개선에 관한 논의는 c-api 태그가 포함된 discuss 포럼 <https://discuss.python.org/c/core-dev/c-api/30> 에서 진행하는 것이 가장 좋습니다.
클래스별 범위¶
현재(Python 3.11 기준) CPython 구현 세부 사항에 의존하지 않고 개별 형(type) 에 상태를 연결하는 것은 불가능합니다(이러한 구현 세부 사항은 향후 변경될 수 있으며, 역설적으로 클래스별 범위에 대한 적절한 해결책을 허용하기 위해 변경될 수도 있습니다).
힙 형으로의 손실 없는 변환¶
힙 형 API는 정적 형에서 “손실 없는” 변환을 위해 설계되지 않았습니다. 즉, 주어진 정적 형과 정확히 동일하게 작동하는 형을 생성하기 위한 용도가 아닙니다.