Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 820 – C API를 위한 통합 슬롯 시스템, PySlot

Author:
Petr Viktorin <encukou at gmail.com>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Created:
19-Dec-2025
Python-Version:
3.15
Post-History:
06-Jan-2026
Resolution:
23-Apr-2025

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Important

This PEP is a historical document. The up-to-date, canonical documentation can now be found at Definition slots.

×

See PEP 1 for how to propose changes.

초록

타입 및 모듈 슬롯을 플래그가 포함된 태그 지정 익명 공용체라는 새로운 구조로 교체합니다. 이를 통해 타입 안전성이 향상되고 새로운 슬롯을 더 나은 하위 호환성을 갖는 방식으로 추가할 수 있습니다.

3.15에서 추가된 API (PyModule_FromSlotsAndSpec() 및 새로운 확장 내보내기 훅)는 새 슬롯을 사용하도록 변경됩니다.

기존 슬롯 구조와 관련 API는 소프트 폐기되었습니다. (즉, 경고 없이 계속 작동하고 완전히 문서화 및 지원되지만, 여기에 새로운 기능은 추가하지 않을 계획입니다.)

배경

Python 3.14의 C API에는 새 객체를 생성할 때 정보를 제공하는 데 사용되는 확장 가능한 구조체가 두 개 있습니다. 즉, PyType_SpecPyModuleDef입니다.

각각에는 해당 구조체에서 Python 객체를 생성하는 C API 함수군이 있습니다. (각 함수군은 시간이 지나면서 추가된 선택적 인자를 받는 하나의 함수로 작동합니다.) 다음과 같습니다.

“입력” 구조체를 런타임 객체와 분리하면 API와 ABI 모두에서 객체의 내부 구조를 불투명하게 유지할 수 있으므로, 향후 CPython 버전이나 대체 구현에서 세부 사항을 변경할 수 있습니다.

두 구조체에는 본질적으로 태그가 지정된 공용체 배열인 slots 필드가 포함되어 있습니다. (int ID로 태그가 지정된 void 포인터입니다.) 이를 통해 향후 확장이 가능해집니다.

새 모듈 생성 API가 PEP 793에서 추가되었습니다. 이 API는 PyModuleDef 구조체 대신 slots 배열만 사용합니다. 기존 PyModuleDef 멤버를 대체하기 위해 이에 대응하는 슬롯 ID를 추가합니다. 예를 들어 모듈 이름은 PyModuleDef.m_name이 아니라 Py_mod_name 슬롯에 지정됩니다. 해당 PEP에서는 다음과 같이 언급합니다.

PyModuleDef_Slot 구조체에는 고정 필드와 비교할 때 몇 가지 단점이 있습니다. 이러한 단점은 해결할 수 있다고 생각하지만, 이 PEP의 범위에서는 제외합니다.

이 제안은 이러한 단점을 해결합니다.

동기

기존 PyModuleDef_SlotPyType_Slot의 주요 단점은 다음과 같습니다.

타입 안전성
void *는 데이터 포인터, 함수 포인터 및 작은 정수에 사용되므로 모든 관련 아키텍처에서 실제로 작동하는 캐스팅이 필요하지만, C에서는 기술적으로 정의되지 않았거나 구현에 따라 정의된 동작입니다.

예를 들어 Py_tp_doc는 문자열을 나타내고, Py_mod_gil은 작은 정수를, Py_tp_repr은 함수를 나타내며, 모두 void*로 캐스팅해야 합니다.

제한적인 하위 호환성
확장에서 현재 인터프리터가 알 수 없는 슬롯 ID를 제공하면 타입 또는 모듈 생성에 실패합니다. 이로 인해 인터프리터가 지원하는 경우에만 적용되어야 하는 “선택적” 기능을 사용하기가 번거로워집니다. 최근 추가된 Py_mod_gilPy_mod_multiple_interpreters슬롯이 좋은 예입니다.

한 가지 우회 방법은 Python 버전을 확인하고 현재 인터프리터보다 이전에 도입된 슬롯을 생략하는 것입니다. 이는 사용자에게 번거롭습니다. 또한 C API의 가능한 비 CPython 구현을 제약하여, 최신 CPython 버전에 도입된 기능을 “체리 피킹”하지 못하게 합니다.

지지 의견

이 PEP는 최종 사용자에게 즉시 도움이 되지 않는다는 점에서 특이합니다. 도움이 되기 훨씬 전부터 마련되어 있어야 합니다. Cython 유지 관리자의 말을 빌리면, Da Woods:

Cython이 이를 즉시 사용하지는 않을 것이라고 생각합니다(적어도 클래스에서는… 분명히 PEP 793에 들어간다면 이를 사용할 것입니다). 이는 즉각적인 새 기능보다는 미래의 개선을 주된 대상으로 하기 때문입니다. 그래도 사용할 수 있어 보입니다.

대신 C API를 변경해야 하며 하위 호환 방식으로 변경하고자 하는 핵심 개발자들로부터 지원이 나옵니다:

  • Mark Shannon:
    이는 가치 있는 개선으로 보입니다. 저를 +1로 세어 주십시오.
  • Victor Stinner:
    생각을 바꾸어 이제 PEP 820의 지지자가 되었습니다 :) 시간이 좀 걸렸지만 이제 PEP 820의 장점을 알겠습니다. 하위 호환성과 안정적인 ABI를 향상합니다.

예제

이 제안은 슬롯 배열로부터 클래스와 모듈을 생성하는 API를 추가하며, 매크로를 사용하여 다음과 같이 C 리터럴로 지정할 수 있습니다.:

static PySlot myClass_slots[] = {
   PySlot_STATIC_DATA(tp_name, "mymod.MyClass"),
   PySlot_SIZE(tp_extra_basicsize, sizeof(struct myClass)),
   PySlot_FUNC(tp_repr, myClass_repr),
   PySlot_INT64(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_DICT),
   PySlot_END,
}

// ...

PyObject *MyClass = PyType_FromSlots(myClass_slots);

매크로는 수작업으로 작성한 리터럴을 간소화합니다. 여러 Python 버전 간의 호환성, 템플릿화되거나 자동 생성된 슬롯 배열과 같은 더 복잡한 사용 사례에서는 물론 C를 사용하지 않는 C API 사용자에게도 슬롯 구조체 정의를 직접 작성할 수 있습니다. 예를 들어 nb_matrix_multiply 슬롯 (PEP 465)이 3.5가 아니라 가까운 미래(예를 들어 CPython 3.17)에 추가되었다면, 사용자는 이를 “OPTIONAL” 플래그와 함께 추가하여 해당 연산자를 지원하는 CPython 버전에서만 클래스가 @ 연산자를 지원하도록 만들 수 있습니다.:

static PySlot myClass_slots[] = {
   ...
   {   // skipped if not supported
       .sl_id=Py_nb_matrix_multiply,
       .sl_flags=PySlot_OPTIONAL,
       .sl_func=myClass_matmul,
   },
   PySlot_END,
}

근거

여기에서는 이 제안의 설계 결정을 설명합니다.

근거의 일부는 PEP 793에서 반복되며, 이 PEP는 PyModuleDef 구조체를 슬롯 배열로 대체했습니다.

슬롯 사용

슬롯의 주요 대안은 입력에 버전이 지정된 struct를 사용하는 것입니다.

이러한 설계에는 두 가지 변형이 있습니다.

  • 모든 정보를 위한 필드를 포함하는 큰 구조체입니다. PyTypeObject에서 볼 수 있듯이 실제로 이러한 구조체의 대부분은 NULL인 경향이 있습니다. 더 많은 필드가 더 이상 사용되지 않게 되면 낭비가 늘어나거나, 새로운 구조체 레이아웃을 도입해야 합니다(한동안 기존 레이아웃과의 호환성을 유지하면서).
  • 초기 생성에 필요한 정보만 포함하는 작은 구조체이며, 나머지 정보는 이후에 추가합니다(전용 함수 호출이나 Python 수준의 setattr 호출을 사용합니다). 이 설계는 다음과 같은 문제를 일으킵니다.
    • 필요한 정보를 추가하거나 폐기하거나 조정하기가 번거롭습니다(예를 들어 PEP 697에서는 기존 필드의 음수 값에 의미를 부여했습니다. 비슷한 상황에서는 새 필드를 추가하는 편이 더 깔끔합니다).
    • 확장과 인터프리터 간 API 호출 수를 늘립니다.

    “배치” 타입/모듈 생성 API는 “실시간” 객체를 수정하는 API와 부분적으로 중복되더라도 타당하다고 생각합니다.

슬롯 사용하기

클래스 PyType_SpecPyModuleDef은 슬롯 배열 외에도 명시적 필드를 갖습니다. 여기에는 다음이 포함됩니다:

  • 클래스 이름(PyType_Spec.name)과 같은 필수 정보입니다. 이 제안은 이름에 대한 슬롯 ID를 추가하고 이를 필수로 만듭니다.
  • 포인터가 아닌 항목(basicsize, flags)입니다. 원래 슬롯은 함수 포인터만 포함하도록 설계되었지만, 이제는 데이터 포인터와 정수 또는 플래그도 포함합니다. 이 제안은 타입을 깔끔하게 처리하기 위해 유니온을 사용합니다.
  • 슬롯 메커니즘 이전에 추가된 항목입니다. PyModuleDef.m_slots자체는 항상 NULL이었던 m_reload에서 용도가 변경되었습니다. 선택적인 m_traverse 또는 m_methods 멤버는 그보다 앞서 존재했습니다.

이러한 필드 없이 오직 슬롯 배열 하나만 사용할 수 있습니다. 배열을 둘러싼 래퍼 클래스는 설계를 복잡하게 만들 것입니다. 이러한 클래스의 필드가 언젠가 더 이상 사용되지 않게 되면 제거하거나 용도를 변경하기 어렵습니다.

중첩된 슬롯 테이블

이 제안에서는 슬롯 배열이 다른 슬롯 배열을 참조할 수 있으며, 이 배열은 해당 “부모”에 재귀적으로 병합된 것처럼 처리됩니다. 이는 인터프리터 내부의 슬롯 처리를 복잡하게 만들지만, 다음을 가능하게 합니다:

  • 동적으로 할당된 (또는 스택에 할당된) 슬롯과 static인 슬롯을 혼합할 수 있습니다. 이는 일반적으로 static일 수 없는 값으로 확장되는 PyType_From* 함수군의 문제를 해결합니다. 예를 들어, PyType_FromModuleAndSpec()module 인자는 힙에 할당된 모듈 객체여야 합니다.
  • 여러 클래스/모듈에 공통되는 기능을 구현하도록 슬롯의 일부를 공유할 수 있습니다.
  • 예를 들어 Python 버전에 따라 일부 슬롯을 조건부로 쉽게 포함할 수 있습니다.

중첩된 “레거시” 슬롯 테이블

중첩된 PyType_Slot 배열과 마찬가지로, “새” 슬롯에서 “레거시” 슬롯(PyType_SlotPyModuleDef_Slot) 배열을 지원하고 그 반대도 지원할 것을 제안합니다.

이렇게 하면 사용자는 다시 작성하거나 서식을 다시 지정하지 않고 이미 작성한 코드를 재사용할 수 있으며, 새로운 기능이 필요한 경우에만 “새” 슬롯을 사용할 수 있습니다.

고정 폭 정수

이 제안은 슬롯 ID(uint16_t)와 플래그(uint64_t)에 고정 폭 정수를 사용합니다. C int 형식을 사용할 경우 16비트를 초과하여 사용하는 것은 이식 가능하지 않지만, 일반적인 플랫폼에서는 조용히 작동합니다. int를 사용하면서 UINT16_MAX를 초과하는 값을 피하면 일반적인 플랫폼에서 16비트를 낭비합니다.

메모리 레이아웃

일반적인 64비트 플랫폼에서는 새 구조체의 크기를 기존 PyType_SlotPyModuleDef_Slot구조체와 동일하게 유지할 수 있습니다. (기존 구조체는 int의 이식성과 패딩으로 인해 16바이트 중 6바이트를 낭비하지만, 이 제안에서는 해당 비트 중 일부를 새로운 기능에 사용합니다.) 32비트 플랫폼에서는 이 제안이 64비트 플랫폼과 동일한 레이아웃을 요구하므로, 기존 구조체에 비해 크기가 2배로 증가합니다(8바이트에서 16바이트로 증가합니다). 일반적으로 static인 “구성” 데이터라면 문제가 없을 것입니다.

이 제안에서는 비트 필드와 열거형을 사용하지 않습니다. 이러한 메모리 표현은 컴파일러에 따라 달라지므로 C 이외의 언어에서 API를 사용할 때 문제가 발생합니다.

이 구조체는 타입의 정렬 요구 사항이 그 크기와 일치한다고 가정하여 배치됩니다.

단일 ID 공간

현재 moduletype 슬롯의 숫자 값은 서로 겹칩니다.

  • Py_bf_getbuffer == Py_mod_create == 1
  • Py_bf_releasebuffer == Py_mod_exec == 2
  • Py_mp_ass_subscript == Py_mod_multiple_interpreters == 3
  • Py_mp_length == Py_mod_gil == 4
  • 그리고 CPython 3.15에서 추가된 모듈 슬롯도 이와 유사합니다.

이 제안에서는 둘 모두에 하나의 시퀀스를 사용하므로, 향후 슬롯에서는 이러한 중복을 피할 수 있습니다. 이는 다음을 위한 것입니다.

  • 모듈에 타입 슬롯을, 반대로 타입에 모듈 슬롯을 실수로 사용하는 것을 방지합니다.
  • 외부 라이브러리 또는 검사기가 ID를 기반으로 슬롯의 의미(및 타입)를 확인할 수 있도록 합니다.

기존에 중복되는 항목이 4개 있으므로 지금 당장은 이러한 목표를 달성할 수 없지만, 사용자에게 투명한 방식으로 새로운 숫자 ID로 점진적으로 마이그레이션할 수 있습니다.

가장 큰 단점은 내부 조회 테이블이 더 커지거나(타입과 모듈에 별도의 테이블을 사용하여 빈 항목이 포함되는 경우), 관리하기 더 어려워진다는 것입니다(테이블을 병합하는 경우).

사용 중단 경고

여러 슬롯은 NULL 값을 허용하지 않는다고 문서화되어 있지만, CPython은 하위 호환성을 위해 NULL을 허용합니다. 마찬가지로 단일 배열에 여러 슬롯 ID가 두 번 이상 나타나서는 안 되지만, CPython은 이러한 중복을 허용합니다.

이는 유지 관리 문제입니다. 구현이 변경되더라도 CPython은 이러한 경우의 문서화되지 않았고(대개 테스트되지도 않은) 동작을 유지해야 하기 때문입니다.

또한 API 확장을 방해합니다. 예를 들어 3.10에서 Py_TPFLAGS_DISALLOW_INSTANTIATION 플래그를 추가하는 대신, 동일한 효과를 위해 Py_tp_new슬롯을 NULL로 설정하도록 허용할 수 있었습니다.

먼 미래에 이러한 예외적인 경우의 동작을 변경할 수 있도록 하고 C API의 가능한 대체 구현에 자유를 부여하기 위해, 이러한 경우 런타임 사용 중단 경고를 발행하기 시작할 것입니다. 사용자가 제어할 수 없는 사항에 대한 경고로 사용자를 inundate하지 않도록, 새로운 API가 사용될 때만 사용 중단 경고를 표시합니다.

사양

다음과 같은 새로운 PySlot구조체를 정의합니다.:

typedef struct PySlot {
    uint16_t sl_id;
    uint16_t sl_flags;
    union {
        uint32_t _sl_reserved;  // must be 0
    };
    union {
        void *sl_ptr;
        void (*sl_func)(void);
        Py_ssize_t sl_size;
        int64_t sl_int64;
        uint64_t sl_uint64;
    };
} PySlot;
  • sl_id: 슬롯의 기능을 식별하는 슬롯 번호입니다.
  • sl_flags: 아래에 정의된 플래그입니다.
  • 향후 확장을 위해 32비트를 예약합니다(향후 플래그를 통해 활성화될 것으로 예상됩니다).
  • 타입이 슬롯에 따라 달라지는 데이터를 포함하는 union입니다.

새로운 API

다음 함수가 추가됩니다. 주어진 슬롯 배열로 해당 Python 타입 객체를 생성합니다.:

PyObject *PyType_FromSlots(const PySlot *slots);

이 함수를 사용하면 Py_tp_token 슬롯을 Py_TP_USE_SPEC(즉, NULL)로 설정할 수 없습니다.

변경된 API

PyModule_FromSlotsAndSpec 함수(PEP 793에 따라 CPython 3.15에 추가됨)는 새 슬롯 구조를 받도록 변경될 것입니다:

PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec)

관련 PEP 793에서 추가된 확장 모듈 내보내기 훅 (PyModExport_<name>)은 새로운 슬롯 구조체를 반환하도록 변경됩니다. 관련 PyMODEXPORT_FUNC 매크로도 이에 맞게 업데이트됩니다.

일반적인 슬롯 의미론

슬롯을 적용하는 함수에 슬롯이 전달되면, 해당 함수는 슬롯 배열이나 그것이 재귀적으로 가리키는 어떠한 데이터도 수정하지 않습니다.

함수가 완료된 후에는 명시적으로 “static”으로 표시된 경우(아래의 PySlot_STATIC 참조)를 제외하고, 사용자가 배열과 배열이 재귀적으로 가리키는 모든 데이터를 수정하거나 할당 해제할 수 있습니다. 즉, 인터프리터는 일반적으로 char * 텍스트를 포함하여 구조체의 모든 데이터를 복사해야 합니다.

플래그

sl_flags에는 다음 비트를 설정할 수 있습니다. 할당되지 않은 비트는 0으로 설정해야 합니다.

  • PySlot_OPTIONAL: 슬롯 ID를 알 수 없는 경우 인터프리터는 해당 슬롯을 완전히 무시해야 합니다. (예를 들어, 지금 CPython에 nb_matrix_multiply를 추가한다면 타입에서 이 플래그를 사용할 수 있습니다.)
  • PySlot_STATIC: 슬롯이 가리키는 모든 데이터는 정적으로 할당되며 상수입니다. 따라서 인터프리터는 해당 정보를 복사할 필요가 없습니다. 함수 포인터에는 이 플래그가 암시됩니다.

    이 플래그는 슬롯이 “간접적으로” 가리키는 데이터에도 적용되지만, 아래의 중첩 슬롯 테이블에 설명된 중첩 슬롯은 예외이며 자체 PySlot_STATIC 플래그를 가질 수 있습니다. 예를 들어 PyMemberDef 구조체의 배열을 가리키는 Py_tp_members 슬롯에 적용하는 경우, 전체 배열과 그 요소의 namedoc 문자열은 모두 정적이며 상수여야 합니다.

    정적 데이터가 필요한 슬롯(Py_mod_methods, Py_tp_methods, Py_tp_members, Py_tp_getset)에는 이 플래그가 필요합니다.

  • PySlot_INTPTR: 데이터는 sl_ptr에 저장되며 적절한 타입으로 캐스팅해야 합니다.

    이 플래그는 모든 슬롯이 이러한 방식으로 작동하는 기존 PyType_SlotPyModuleDef_Slot에서 포팅하는 작업을 간소화합니다.

편의 매크로

슬롯 정의를 간소화하기 위해 다음 매크로가 API에 추가됩니다.:

#define PySlot_DATA(NAME, VALUE) \
   {.sl_id=NAME, .sl_ptr=(void*)(VALUE)}

#define PySlot_FUNC(NAME, VALUE) \
   {.sl_id=NAME, .sl_func=(VALUE)}

#define PySlot_SIZE(NAME, VALUE) \
   {.sl_id=NAME, .sl_size=(VALUE)}

#define PySlot_INT64(NAME, VALUE) \
   {.sl_id=NAME, .sl_int64=(VALUE)}

#define PySlot_UINT64(NAME, VALUE) \
   {.sl_id=NAME, .sl_uint64=(VALUE)}

#define PySlot_STATIC_DATA(NAME, VALUE) \
   {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}

#define PySlot_END {0}

또한 명명된 초기화자를 사용하지 않는 매크로 두 개를 추가하여 C++11 호환 코드에서 사용할 수 있도록 합니다. 이 매크로는 값을 void*로 캐스팅하므로 기존 슬롯보다 타입 안전성을 향상시키지 않는다는 점에 유의하십시오.:

#define PySlot_PTR(NAME, VALUE) \
   {NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}}

#define PySlot_PTR_STATIC(NAME, VALUE) \
   {NAME, PySlot_INTPTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}}

중첩 슬롯 테이블

슬롯 테이블의 중첩을 허용하기 위해 새 슬롯인 Py_slot_subslots가 추가됩니다. 이 값(sl_ptr)은 PySlot 구조체 배열을 가리켜야 하며, 이 배열은 현재 슬롯 배열의 일부인 것처럼 처리됩니다. sl_ptrNULL로 설정하여 슬롯이 없음을 나타낼 수 있습니다.

기존 슬롯 구조체에도 유사한 중첩을 허용하는 슬롯 두 개가 추가됩니다.

  • Py_tp_slots: PyType_Slot 배열용
  • Py_mod_slotsPyModuleDef_Slot 배열을 위한 것입니다.

배열의 각 PyType_Slot(PySlot){.sl_id=slot, .sl_flags=PySlot_INTPTR, .sl_ptr=func}로 변환되며, 필요한 슬롯에는 sl_flagsPySlot_STATIC이 추가됩니다. PyModuleDef_Slot도 마찬가지입니다.

초기 구현에서는 중첩 깊이를 5단계로 제한합니다. 이 제한은 향후 해제될 수 있습니다.

새로운 슬롯 ID

타입 및 모듈 정의 모두에 사용할 수 있는 다음 새로운 슬롯 ID가 추가됩니다.

  • Py_slot_end(0으로 정의됨): 슬롯 배열의 끝을 표시합니다.
    • PySlot_INTPTRPySlot_STATIC 플래그는 무시됩니다.
    • PySlot_OPTIONAL 플래그는 Py_slot_end와 함께 사용할 수 없습니다.
  • Py_slot_subslots, Py_tp_slots, Py_mod_slots: 위의 중첩 슬롯 테이블 항목을 참조하십시오.
  • Py_slot_invalid(UINT16_MAX, 즉 -1로 정의됨): 알 수 없는 슬롯 ID로 처리됩니다.

PyModuleDef의 기존 멤버를 포괄하기 위해 다음 새로운 슬롯 ID가 추가됩니다.

  • Py_tp_name(타입 생성에 필수)
  • Py_tp_basicsize(Py_ssize_t 타입)
  • Py_tp_extra_basicsize(PyType_Spec.basicsize-extra_basicsize로 설정하는 것과 동일함)
  • Py_tp_itemsize
  • Py_tp_flags

PyType_FromMetaclass의 인자를 포괄하기 위해 다음 새로운 슬롯 ID가 추가됩니다.

  • Py_tp_metaclass(메타클래스 계산 후 ob_type을 설정하는 데 사용됨)
  • Py_tp_module

Py_tp_basePy_tp_bases는 이미 존재한다는 점에 유의하십시오. 인터프리터는 이들을 동일하게 처리합니다. 둘 중 어느 것이든 클래스 객체 또는 클래스 객체의 튜플을 지정할 수 있습니다. Py_tp_basePy_tp_bases를 대신하여 소프트 폐기됩니다. 하나의 정의에서 둘 다 지정하는 것은 폐기될 예정입니다(현재는 Py_tp_basesPy_tp_base를 재정의합니다).

새로운 슬롯 중 어느 것도 PyType_GetSlot과 함께 사용할 수 없습니다. (C API WG의 승인을 받으면 이 제한이 향후 해제될 수 있습니다.)

새로운 슬롯 중 Py_slot_end, Py_slot_subslots, Py_tp_slots, Py_mod_slotsPyType_Spec 및/또는 PyModuleDef에서 허용됩니다.

슬롯 번호 변경

새로운 슬롯 ID는 고유한 숫자 값을 갖습니다(즉, Py_slot_*, Py_tp_*Py_mod_*는 ID를 공유하지 않습니다).

1부터 4까지 번호가 매겨진 슬롯(Py_bf_getbufferPy_mp_lengthPy_mod_createPy_mod_gil)은 새로운 더 큰 번호로 재정의됩니다. 이전 번호는 별칭으로 남으며, 3.15 미만의 Stable ABI 버전용으로 컴파일할 때 사용됩니다.

PyModuleDef의 멤버를 위한 슬롯은 PEP 793에서 추가되었으며, 고유한 ID를 갖도록 번호가 다시 매겨집니다:

  • Py_mod_name
  • Py_mod_doc
  • Py_mod_state_size
  • Py_mod_methods
  • Py_mod_state_traverse
  • Py_mod_state_clear
  • Py_mod_state_free

소프트 폐기

다음 기존 함수들은 soft-deprecated됩니다:

  • PyType_FromSpec
  • PyType_FromSpecWithBases
  • PyType_FromModuleAndSpec
  • PyType_FromMetaclass
  • PyModule_FromDefAndSpec
  • PyModule_FromDefAndSpec2
  • PyModule_ExecDef

(다시 말씀드리면, 소프트 폐기된 API는 제거될 예정이 아니며, 경고를 발생시키지 않고, 계속 문서화되고 테스트됩니다. 그러나 여기에 새로운 기능은 추가되지 않습니다.)

이러한 함수가 허용하는 PyType_Slot 또는 PyModuleDef_Slot 배열에는 이 PEP에서 정의된 “새로운” 슬롯을 비롯하여 모든 슬롯이 포함될 수 있습니다. 여기에는 중첩된 “새 스타일” 슬롯(Py_slot_subslots)도 포함됩니다.

사용 중단 경고

PySlot 배열을 받는 함수는 (기존의 PyType_Slot 또는 PyModuleDef_Slot 배열을 받는 함수와 달리) 현재 문서에서는 허용되지 않지만 런타임에서는 허용되는 다음 경우에 해당하는 슬롯에 대해 런타임 사용 중단 경고를 발생시킵니다:

  • 슬롯 값을 NULL로 설정하는 경우:
    • Py_tp_doc를 제외한 모든 타입 슬롯
    • Py_mod_create
    • Py_mod_exec
  • 하나의 슬롯 배열에서 슬롯 ID를 반복하는 경우 (이 PEP에서 추가된 하위 슬롯 배열 포함):
    • 이미 런타임 오류인 경우(Py_tp_doc, Py_tp_members)를 제외한 모든 타입 슬롯
    • Py_mod_create
    • Py_mod_abi

하위 호환성

이 PEP는 Python 3.15의 알파 버전에서 이미 릴리스된 API를 변경할 것을 제안합니다. 이 API의 초기 사용자는 불편을 겪겠지만, PEP가 첫 번째 베타 버전 이전에 승인되고 구현되는 한 이 변경은 하위 호환성 정책의 문언과 취지에 부합합니다.

슬롯의 번호 재지정은 하위 호환성을 유지하는 방식으로 수행합니다. 이전 값은 계속 허용되며, 더 이전 Stable ABI에 맞게 컴파일할 때 사용합니다.

허용되지 않는 것으로 문서화된 일부 사례에서는 사용 중단 경고를 내기 시작합니다(자세한 내용은 사용 중단 경고를 참조하십시오).

그 외에 이 PEP는 API를 추가하고 소프트 디프리케이트할 뿐이므로 하위 호환성을 유지합니다.

보안 관련 사항

알려진 사항 없음

이 내용을 가르치는 방법

이를 사용하도록 “확장 및 임베딩” 튜토리얼을 조정하십시오.

참조 구현

이 PEP가 승인된 후 구현이 CPython issue 149044에 병합되었습니다.

거부된 아이디어

여러 대안 아이디어는 근거 절을 참조하십시오.

서드파티 슬롯 ID 할당

서드파티가 자체 용도로 슬롯 ID를 예약할 수 있도록 하자는 제안이 있었습니다. 이는 주로 대체 구현에 유용합니다. 예를 들어 GraalPy와 같은 구현에서는 사용자 지정 타입 슬롯(예: “이 Java 클래스에서 상속” 슬롯)이 필요할 수 있습니다. 이와 유사하게 한때 PyPy의 typeobject 구조체에는 추가 tp_pypy_flags가 있었습니다.

이 PEP는 네임스페이스 메커니즘을 지정하지 않습니다. 이는 향후 추가할 수 있습니다. 또한 대체 구현을 위해 개별 슬롯 ID를 예약할 수도 있습니다.

슬롯은 extension modules가 타입이나 모듈에 추가 데이터를 추가하는 데 적합한 방법이 아니라는 점에 유의하십시오. 특정 객체를 생성하는 데 사용된 슬롯을 조회할 API가 없기 때문입니다.

익명 유니온 방지

이 PEP는 anonymous unions를 포함하는 구조체를 제안하지만, 이는 아직 CPython의 문서화된 공개 API에서 사용되지 않습니다.

이를 추가해도 알려진 문제는 없지만, 다음 사항이 관련될 수 있습니다.

  • 익명 유니온은 C11부터 C에서만 지원됩니다. 그러나 CPython은 이미 이 기능을 요구하며, PyObject 구조체의 내부 멤버에 사용합니다.
  • C 스타일 지정 초기화자를 추가하는 C++20 이전에는 C++ 초기화자가 유니온의 첫 번째 멤버만 설정할 수 있습니다. 그러나 이는 named 유니온에서도 발생하는 문제입니다. 유니온을 완전히 피하면 이 PEP의 타입 안전성 개선 사항 대부분을 잃게 됩니다.

    제안된 플래그 PySlot_INTPTR와 해결 방법 매크로 PySlot_PTRPySlot_PTR_STATIC을 사용하면 C++11과 호환되어야 하거나 유니온과 관련된 유사한 제한이 있는 코드에서도 이 API를 사용할 수 있다는 점에 유의하십시오.

  • C++에는 익명 structs가 없습니다. 익명 구조체/유니온을 하나의 언어 기능으로 인식하는 C 프로그래머에게는 이것이 놀라울 수 있습니다.
  • C/C++가 아닌 언어의 래퍼는 유니온에 이름을 지정해야 할 수 있습니다. 괜찮습니다. (독자 여러분, 이것이 필요하다면 헤더와 문서에서 선호되는 이름을 노출하는 방법에 관한 CPython 이슈를 열어 주십시오.)

더 큰 관점에서 보면, 익명 유니온은 태그 유니온을 구현하고 하위 호환 방식으로 공개 API를 발전시키는 데 유용한 도구가 될 수 있습니다. 이 PEP는 익명 유니온을 더 자주 사용할 수 있는 길을 의도적으로 열어 둡니다.

폴백 슬롯

이 PEP의 이전 버전에서는 다음 플래그를 제안했습니다: PySlot_HAS_FALLBACK:

플래그가 지정된 슬롯의 ID를 알 수 없으면 인터프리터는 해당 슬롯을 무시합니다. ID를 알고 있다면 인터프리터는 HAS_FALLBACK이 없는 첫 번째 슬롯까지(그리고 그 슬롯을 포함하여) 뒤따르는 슬롯을 무시해야 합니다.

사실상 HAS_FALLBACK 플래그가 지정된 연속 슬롯들과 그 뒤의 첫 번째 non-HAS_FALLBACK 슬롯은 하나의 “블록”을 형성하며, 인터프리터는 해당 블록에서 자신이 이해하는 첫 번째 슬롯만 고려합니다. 블록 전체를 선택 사항으로 만들려면 OPTIONAL 플래그가 지정된 슬롯으로 끝나야 합니다.

이 플래그는 필요한 Python 버전에서 나중에 추가될 수 있습니다. (하위 호환성을 위해 HAS_FALLBACK이 지정된 모든 슬롯에는 OPTIONAL 플래그도 필요합니다.)

미해결 문제

아직 없습니다.

감사의 말

이번 제안 개정에 상당한 의견을 보내 주신 Da Woods, Antoine Pitrou, Mark Shannon 및 Victor Stinner께 감사드립니다.

변경 이력

  • 2026년 6월 17일
    • 정적 데이터가 필요한 슬롯에는 명시적인 PySlot_STATIC 플래그를 요구합니다.
    • PEP가 최종본으로 표시되었습니다.
  • 2026년 4월 24일
    • NULL 및 반복 슬롯에 대한 사용 중단을 새 API로 제한합니다.
    • PEP가 승인되었습니다.
  • 2026년 3월 12일 - 불필요한 PySlot_HAS_FALLBACK 플래그를 제거합니다.
  • 2026년 1월 28일
    • 3.15 알파 버전에 추가된 PEP 793 API(PyModExport, PyModule_FromSlotsAndSpec)가 새 슬롯을 반환하도록 변경된다는 점을 더 명확히 합니다.
    • 작동하지 않는 것으로 문서화되었지만 실제로는 작동했던 항목의 사용 중단:
      • 슬롯 값을 NULL로 설정합니다(슬롯에서 이를 명시적으로 허용하는 경우는 제외합니다).
      • 정의에서 슬롯 ID를 반복합니다(제외 )
    • “타사 슬롯 ID 할당”과 “익명 유니온 피하기”를 거부된 아이디어에 추가합니다.
  • 2025년 1월 6일 - 최초 PEP