2. 확장형 정의하기: 자습서
**************************

파이썬은 C 확장 모듈 작성자가 내장 "str"과 "list" 형과 마찬가지로 파이
썬 코드에서 조작할 수 있는 새로운 형을 정의할 수 있도록 합니다. 모든
확장형의 코드는 패턴을 따르지만, 시작하기 전에 이해해야 할 세부 사항이
있습니다. 이 설명서는 주제에 대한 간단한 소개입니다.


2.1. 기초
=========

*CPython* 런타임은 모든 파이썬 객체를 "PyObject*" 형의 변수로 간주하는
데, 이는 모든 파이썬 객체의 "베이스형" 역할을 합니다. "PyObject" 구조
체 자체는 객체의 *참조 횟수*와 객체의 "형 객체"에 대한 포인터만 포함합
니다. 여기가 액션이 일어나는 곳입니다; 형 객체는 예를 들어 객체에서 어
트리뷰트를 조회하거나, 메서드를 호출하거나, 다른 객체와 곱할 때 인터프
리터가 호출하는 (C) 함수를 결정합니다. 이러한 C 함수를 "형 메서드"라고
합니다.

따라서, 새 확장형을 정의하려면, 새 형 객체를 만들어야 합니다.

이런 종류의 것은 예제로만 설명할 수 있어서, 여기에 C 확장 모듈
"custom" 내에서 "Custom"이라는 새 형을 정의하는 최소한이지만 완전한 모
듈이 있습니다:

참고:

  여기에 표시하는 것은 *정적인(static)* 확장형을 정의하는 전통적인 방
  법입니다. 대부분의 용도에 적합해야 합니다. C API는 또한
  "PyType_FromSpec()" 함수를 사용하여 힙 할당 확장형을 정의 할 수 있습
  니다만, 이 자습서에서는 다루지 않습니다.

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   typedef struct {
       PyObject_HEAD
       /* Type-specific fields go here. */
   } CustomObject;

   static PyTypeObject CustomType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       .tp_name = "custom.Custom",
       .tp_doc = PyDoc_STR("Custom objects"),
       .tp_basicsize = sizeof(CustomObject),
       .tp_itemsize = 0,
       .tp_flags = Py_TPFLAGS_DEFAULT,
       .tp_new = PyType_GenericNew,
   };

   static PyModuleDef custommodule = {
       PyModuleDef_HEAD_INIT,
       .m_name = "custom",
       .m_doc = "Example module that creates an extension type.",
       .m_size = -1,
   };

   PyMODINIT_FUNC
   PyInit_custom(void)
   {
       PyObject *m;
       if (PyType_Ready(&CustomType) < 0)
           return NULL;

       m = PyModule_Create(&custommodule);
       if (m == NULL)
           return NULL;

       Py_INCREF(&CustomType);
       if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
           Py_DECREF(&CustomType);
           Py_DECREF(m);
           return NULL;
       }

       return m;
   }

이제는 한 번에 배워야 할 것이 많지만, 이전 장과 비슷해 보이기를 바랍니
다. 이 파일은 세 가지를 정의합니다:

1. "Custom" **객체**에 포함된 것: "CustomObject" 구조체이며, 각
   "Custom" 인스턴스마다 한 번씩 할당됩니다.

2. "Custom" **형**의 작동 방식: "CustomType" 구조체이며, 특정 연산이
   요청될 때 인터프리터가 검사하는 플래그와 함수 포인터 집합을 정의합
   니다.

3. "custom" 모듈을 초기화하는 방법: "PyInit_custom" 함수와 관련
   "custommodule" 구조체입니다.

첫 번째 것은:

   typedef struct {
       PyObject_HEAD
   } CustomObject;

This is what a Custom object will contain.  "PyObject_HEAD" is
mandatory at the start of each object struct and defines a field
called "ob_base" of type "PyObject", containing a pointer to a type
object and a reference count (these can be accessed using the macros
"Py_TYPE" and "Py_REFCNT" respectively).  The reason for the macro is
to abstract away the layout and to enable additional fields in debug
builds.

참고:

  "PyObject_HEAD" 매크로 뒤에는 세미콜론이 없습니다. 실수로 추가하는
  것에 주의하십시오: 일부 컴파일러는 불평할 것입니다.

물론, 객체는 일반적으로 표준 "PyObject_HEAD" 관용구 외에 추가 데이터를
저장합니다; 예를 들어, 표준 파이썬 floats에 대한 정의는 다음과 같습니
다:

   typedef struct {
       PyObject_HEAD
       double ob_fval;
   } PyFloatObject;

두 번째 것은 형 객체의 정의입니다.

   static PyTypeObject CustomType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       .tp_name = "custom.Custom",
       .tp_doc = PyDoc_STR("Custom objects"),
       .tp_basicsize = sizeof(CustomObject),
       .tp_itemsize = 0,
       .tp_flags = Py_TPFLAGS_DEFAULT,
       .tp_new = PyType_GenericNew,
   };

참고:

  신경 쓰지 않는 모든 "PyTypeObject" 필드를 나열하지 않고 필드의 선언
  순서를 신경 쓰지 않으려면, 위와 같이 C99 스타일의 지명(designated)
  초기화자를 사용하는 것이 좋습니다.

"object.h"에 있는 "PyTypeObject"의 실제 정의는 위의 정의보다 더 많은
필드를 갖습니다. 나머지 필드는 C 컴파일러에 의해 0으로 채워지며, 필요
하지 않으면 명시적으로 지정하지 않는 것이 일반적입니다.

한 번에 한 필드씩 따로 다루려고 합니다:

   PyVarObject_HEAD_INIT(NULL, 0)

이 줄은 위에서 언급한 "ob_base" 필드를 초기화하기 위한 필수 상용구입니
다.

   .tp_name = "custom.Custom",

우리 형의 이름. 이것은 객체의 기본 텍스트 표현과 일부 에러 메시지에 나
타납니다, 예를 들어:

   >>> "" + custom.Custom()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: can only concatenate str (not "custom.Custom") to str

이름이 모듈 이름과 모듈 내 형의 이름을 모두 포함하는 점으로 구분된 이
름임에 유의하십시오. 이 경우 모듈은 "custom"이고 형은 "Custom"이라서,
형 이름을 "custom.Custom"으로 설정합니다. 형이 "pydoc"과 "pickle" 모듈
과 호환되도록 하려면 실제 점으로 구분된 임포트 경로를 사용하는 것이 중
요합니다.

   .tp_basicsize = sizeof(CustomObject),
   .tp_itemsize = 0,

이것은 새로운 "Custom" 인스턴스를 만들 때 파이썬이 할당할 메모리양을
알 수 있도록 하기 위한 것입니다. "tp_itemsize"는 가변 크기 객체에만 사
용되며 그렇지 않으면 0이어야 합니다.

참고:

  파이썬에서 형을 서브 클래싱 할 수 있기를 원하고, 형이 베이스형과 같
  은 "tp_basicsize"를 가지면, 다중 상속에 문제가 있을 수 있습니다. 형
  의 파이썬 서브 클래스는 "__bases__"에 이 형을 먼저 나열해야 합니다,
  그렇지 않으면 에러 없이 형의 "__new__()" 메서드를 호출할 수 없습니다
  . 형이 베이스형보다 큰 "tp_basicsize" 값을 갖도록 하여 이 문제점을
  피할 수 있습니다. 대부분의 경우, 이것은 어쨌든 만족하는데, 베이스형
  이 "object"이거나, 그렇지 않으면 베이스형에 데이터 멤버를 추가하여
  크기를 늘리기 때문입니다.

클래스 플래그를 "Py_TPFLAGS_DEFAULT"로 설정합니다.

   .tp_flags = Py_TPFLAGS_DEFAULT,

모든 형은 이 상수를 플래그에 포함해야 합니다. 적어도 파이썬 3.3까지 정
의된 모든 멤버를 활성화합니다. 추가 멤버가 필요하면, 해당 플래그를 OR
해야 합니다.

"tp_doc"에 형의 독스트링을 제공합니다.

   .tp_doc = PyDoc_STR("Custom objects"),

객체 생성을 가능하게 하려면, "tp_new" 처리기를 제공해야 합니다. 이것은
파이썬 메서드 "__new__()"와 동등하지만, 명시적으로 지정해야 합니다. 이
경우에는, API 함수 "PyType_GenericNew()"에서 제공하는 기본 구현을 그냥
사용할 수 있습니다.

   .tp_new = PyType_GenericNew,

"PyInit_custom()"의 일부 코드를 제외하고, 파일의 다른 모든 내용은 익숙
해야 합니다:

   if (PyType_Ready(&CustomType) < 0)
       return;

이것은 "Custom" 형을 초기화하는데, 처음에 "NULL"로 설정한 "ob_type"을
포함하여, 여러 멤버를 적절한 기본값으로 채웁니다.

   Py_INCREF(&CustomType);
   if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
       Py_DECREF(&CustomType);
       Py_DECREF(m);
       return NULL;
   }

이것은 형을 모듈 딕셔너리에 추가합니다. "Custom" 클래스를 호출하여
"Custom" 인스턴스를 만들 수 있도록 합니다:

   >>> import custom
   >>> mycustom = custom.Custom()

이게 전부입니다! 남아있는 것은 빌드하는 것입니다; 위의 코드를
"custom.c"라는 파일에 넣고:

   from distutils.core import setup, Extension
   setup(name="custom", version="1.0",
         ext_modules=[Extension("custom", ["custom.c"])])

를 "setup.py"라는 파일에 넣은 다음; 다음을

   $ python setup.py build

셸에서 입력하면 서브 디렉터리에 파일 "custom.so"를 생성해야 합니다; 해
당 디렉터리로 이동하여 파이썬을 시작하십시오 --- "import custom" 할 수
있고 Custom 객체로 놀 수 있습니다.

그렇게 어렵지 않습니다, 그렇지 않나요?

물론, 현재 Custom 형은 그리 흥미롭지 않습니다. 데이터가 없고 아무것도
하지 않습니다. 서브 클래싱조차 할 수 없습니다.

참고:

  이 설명서는 C 확장을 빌드하기 위한 표준 "distutils" 모듈을 보여 주지
  만, 실제 사용 사례에서는 새롭고 유지 관리가 잘 된 "setuptools" 라이
  브러리를 사용하는 것이 좋습니다. 이 작업을 수행하는 방법에 대한 설명
  서는 이 문서의 범위를 넘어서고 파이썬 패키징 사용자 지침서에서 찾을
  수 있습니다.


2.2. 기초 예제에 데이터와 메서드 추가하기
=========================================

데이터와 메서드를 추가하도록 기초 예제를 확장해 봅시다. 형을 베이스 클
래스로도 사용할 수 있도록 합시다. 다음 기능을 추가하는 새 모듈
"custom2"를 만들 것입니다:

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>
   #include "structmember.h"

   typedef struct {
       PyObject_HEAD
       PyObject *first; /* first name */
       PyObject *last;  /* last name */
       int number;
   } CustomObject;

   static void
   Custom_dealloc(CustomObject *self)
   {
       Py_XDECREF(self->first);
       Py_XDECREF(self->last);
       Py_TYPE(self)->tp_free((PyObject *) self);
   }

   static PyObject *
   Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       CustomObject *self;
       self = (CustomObject *) type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyUnicode_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->last = PyUnicode_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->number = 0;
       }
       return (PyObject *) self;
   }

   static int
   Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
   {
       static char *kwlist[] = {"first", "last", "number", NULL};
       PyObject *first = NULL, *last = NULL, *tmp;

       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                        &first, &last,
                                        &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_XDECREF(tmp);
       }
       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_XDECREF(tmp);
       }
       return 0;
   }

   static PyMemberDef Custom_members[] = {
       {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
        "first name"},
       {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
        "last name"},
       {"number", T_INT, offsetof(CustomObject, number), 0,
        "custom number"},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
   {
       if (self->first == NULL) {
           PyErr_SetString(PyExc_AttributeError, "first");
           return NULL;
       }
       if (self->last == NULL) {
           PyErr_SetString(PyExc_AttributeError, "last");
           return NULL;
       }
       return PyUnicode_FromFormat("%S %S", self->first, self->last);
   }

   static PyMethodDef Custom_methods[] = {
       {"name", (PyCFunction) Custom_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

   static PyTypeObject CustomType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       .tp_name = "custom2.Custom",
       .tp_doc = PyDoc_STR("Custom objects"),
       .tp_basicsize = sizeof(CustomObject),
       .tp_itemsize = 0,
       .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
       .tp_new = Custom_new,
       .tp_init = (initproc) Custom_init,
       .tp_dealloc = (destructor) Custom_dealloc,
       .tp_members = Custom_members,
       .tp_methods = Custom_methods,
   };

   static PyModuleDef custommodule = {
       PyModuleDef_HEAD_INIT,
       .m_name = "custom2",
       .m_doc = "Example module that creates an extension type.",
       .m_size = -1,
   };

   PyMODINIT_FUNC
   PyInit_custom2(void)
   {
       PyObject *m;
       if (PyType_Ready(&CustomType) < 0)
           return NULL;

       m = PyModule_Create(&custommodule);
       if (m == NULL)
           return NULL;

       Py_INCREF(&CustomType);
       if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
           Py_DECREF(&CustomType);
           Py_DECREF(m);
           return NULL;
       }

       return m;
   }

이 버전의 모듈에는 여러 가지 변경 사항이 있습니다.

다음과 같은 추가 포함(include)을 추가했습니다:

   #include <structmember.h>

이 포함은 나중에 설명하는 것처럼 어트리뷰트를 처리하는 데 사용하는 선
언을 제공합니다.

"Custom" 형은 이제 C 구조체에 *first*, *last* 및 *number*의 세 가지 데
이터 어트리뷰트가 있습니다. *first*와 *last* 변수는 이름과 성을 포함하
는 파이썬 문자열입니다. *number* 어트리뷰트는 C 정수입니다.

객체 구조체는 다음과 같이 갱신됩니다:

   typedef struct {
       PyObject_HEAD
       PyObject *first; /* first name */
       PyObject *last;  /* last name */
       int number;
   } CustomObject;

이제 관리할 데이터가 있기 때문에, 객체 할당과 할당 해제에 관해 더욱 신
중해야 합니다. 최소한, 할당 해제 메서드가 필요합니다:

   static void
   Custom_dealloc(CustomObject *self)
   {
       Py_XDECREF(self->first);
       Py_XDECREF(self->last);
       Py_TYPE(self)->tp_free((PyObject *) self);
   }

이는 "tp_dealloc" 멤버에 대입됩니다:

   .tp_dealloc = (destructor) Custom_dealloc,

이 메서드는 먼저 두 파이썬 어트리뷰트의 참조 횟수를 지웁니다.
"Py_XDECREF()"는 인자가 "NULL"인 경우("tp_new"가 중간에 실패하면 발생
할 수 있습니다)를 올바르게 처리합니다. 그런 다음 객체 형
("Py_TYPE(self)"로 계산합니다)의 "tp_free" 멤버를 호출하여 객체의 메모
리를 해제합니다. 객체 형이 "CustomType"이 아닐 수 있음에 유의하십시오,
객체는 서브 클래스의 인스턴스일 수 있기 때문입니다.

참고:

  "CustomObject *" 인자를 취하도록 "Custom_dealloc"을 정의했지만,
  "tp_dealloc" 함수 포인터는 "PyObject *" 인자를 받을 것으로 기대하기
  때문에 위의 "destructor"로의 명시적 캐스트가 필요합니다. 그렇지 않으
  면, 컴파일러에서 경고가 발생합니다. 이것이 C로 하는 객체 지향 다형성
  입니다!

우리는 성과 이름이 빈 문자열로 초기화되도록 하고 싶어서, "tp_new" 구현
을 제공합니다:

   static PyObject *
   Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       CustomObject *self;
       self = (CustomObject *) type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyUnicode_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->last = PyUnicode_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->number = 0;
       }
       return (PyObject *) self;
   }

그리고 그것을 "tp_new" 멤버에 설치합니다:

   .tp_new = Custom_new,

"tp_new" 처리기는 형의 객체를 (초기화와 대비하여) 생성하는 책임을 집니
다. 파이썬에서 "__new__()" 메서드로 노출됩니다. "tp_new" 멤버를 정의할
필요는 없으며, 실제로 많은 확장형은 위의 "Custom" 형의 첫 번째 버전에
서처럼 "PyType_GenericNew()"를 재사용하기만 합니다. 지금은, "tp_new"
처리기를 사용하여 "first"와 "last" 어트리뷰트를 "NULL"이 아닌 기본값으
로 초기화합니다.

"tp_new"는 인스턴스 화 되는 형(서브 클래스가 인스턴스 화 되면, 반드시
"CustomType"일 필요는 없습니다)과 형이 호출될 때 전달된 모든 인자가 전
달되며, 만들어진 인스턴스를 반환할 것으로 기대됩니다. "tp_new" 처리기
는 항상 위치와 키워드 인자를 받아들이지만, 종종 인자를 무시하고 인자
처리를 초기화 (C의 "tp_init"나 파이썬의 "__init__") 메서드에게 남겨둡
니다.

참고:

  인터프리터가 직접 할 것이라서, "tp_new"는 명시적으로 "tp_init"를 호
  출하면 안 됩니다.

"tp_new" 구현은 "tp_alloc" 슬롯을 호출하여 메모리를 할당합니다:

   self = (CustomObject *) type->tp_alloc(type, 0);

메모리 할당이 실패할 수 있어서, 진행하기 전에 "tp_alloc" 결과가 "NULL"
이 아닌지 확인해야 합니다.

참고:

  우리는 "tp_alloc" 슬롯을 직접 채우지 않았습니다. 대신
  "PyType_Ready()"가 베이스 클래스(기본적으로 "object"입니다)에서 상속
  하여 이를 채웁니다. 대부분의 형은 기본 할당 전략을 사용합니다.

참고:

  협업 "tp_new"(베이스형의 "tp_new"나 "__new__()"를 호출하는 것)를 만
  드는 경우, 실행 시간에 메서드 결정 순서를 사용하여 호출할 메서드를
  결정하려고 하지 *않아야* 합니다. 항상 어떤 형을 호출할지 정적으로 결
  정하고, 그것의 "tp_new"를 직접, 또는 "type->tp_base->tp_new"를 통해
  호출하십시오. 이렇게 하지 않으면, 다른 파이썬 정의 클래스도 상속하는
  여러분 형의 파이썬 서브 클래스가 올바르게 작동하지 않을 수 있습니다.
  (특히, "TypeError"를 얻지 않으면서, 이러한 서브 클래스의 인스턴스를
  만들지 못할 수도 있습니다.)

인스턴스의 초깃값을 제공하는 인자를 받아들이는 초기화 함수도 정의합니
다:

   static int
   Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
   {
       static char *kwlist[] = {"first", "last", "number", NULL};
       PyObject *first = NULL, *last = NULL, *tmp;

       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                        &first, &last,
                                        &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_XDECREF(tmp);
       }
       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_XDECREF(tmp);
       }
       return 0;
   }

이것으로 "tp_init" 슬롯을 채웁니다:

   .tp_init = (initproc) Custom_init,

"tp_init" 슬롯은 파이썬에서 "__init__()" 메서드로 노출됩니다. 객체가
만들어진 후 초기화하는 데 사용됩니다. 초기화자는 항상 위치와 키워드 인
자를 받아들이며 성공 시 "0" 또는 에러 시 "-1"을 반환해야 합니다.

"tp_new" 처리기와 달리, "tp_init"가 아예 호출되지 않을 수도 있습니다 (
예를 들어, "pickle" 모듈은 기본적으로 역 피클 된 인스턴스에서
"__init__()"를 호출하지 않습니다). 여러 번 호출될 수도 있습니다. 누구
나 우리 객체의 "__init__()" 메서드를 호출할 수 있습니다. 이런 이유로,
새 어트리뷰트 값을 대입할 때는 각별히 주의해야 합니다. 예를 들어
"first" 멤버를 다음과 같이 대입하려고 할 수 있습니다:

   if (first) {
       Py_XDECREF(self->first);
       Py_INCREF(first);
       self->first = first;
   }

하지만 이것은 위험합니다. 우리 형은 "first" 멤버의 형을 제한하지 않아
서, 모든 종류의 객체가 될 수 있습니다. "first" 멤버에 액세스하려고 시
도하는 코드가 실행되도록 하는 파괴자가 있을 수 있습니다; 또는 파괴자가
*전역 인터프리터 록*을 해제하고 다른 스레드에서 객체에 액세스하고 수정
하는 임의의 코드가 실행되도록 할 수 있습니다.

편집증적이 되고 이 가능성으로부터 우리 자신을 보호하기 위해, 우리는 거
의 항상 참조 횟수를 줄이기 전에 멤버를 다시 대입합니다. 언제 이렇게 하
지 않아도 될까요?

* 참조 횟수가 1보다 크다는 것을 확실히 알고 있을 때;

* 객체의 할당 해제가 *GIL*을 해제하지도 않고 형의 코드를 다시 호출하지
  도 않음을 알고 있을 때 [1];

* 순환 가비지 수거를 지원하지 않는 형의 "tp_dealloc" 처리기에서 참조
  횟수를 감소시킬 때 [2].

인스턴스 변수를 어트리뷰트로 노출하려고 합니다. 이를 수행하는 방법에는
여러 가지가 있습니다. 가장 간단한 방법은 멤버 정의를 정의하는 것입니다
:

   static PyMemberDef Custom_members[] = {
       {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
        "first name"},
       {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
        "last name"},
       {"number", T_INT, offsetof(CustomObject, number), 0,
        "custom number"},
       {NULL}  /* Sentinel */
   };

그리고 "tp_members" 슬롯에 정의를 넣습니다:

   .tp_members = Custom_members,

각 멤버 정의에는 멤버 이름, 형, 오프셋, 액세스 플래그 및 독스트링이 있
습니다. 자세한 내용은 아래 범용 어트리뷰트 관리 섹션을 참조하십시오.

이 접근법의 단점은 파이썬 어트리뷰트에 대입할 수 있는 객체의 형을 제한
할 방법을 제공하지 않는다는 것입니다. 이름과 성은 문자열일 것으로 기대
하지만, 모든 파이썬 객체를 할당할 수 있습니다. 또한 어트리뷰트를 삭제
할 수 있습니다, C 포인터를 "NULL"로 설정합니다. "NULL"이 아닌 값으로
멤버를 초기화 할 수 있지만, 어트리뷰트를 삭제하면 멤버를 "NULL"로 설정
할 수 있습니다.

이름과 성을 이어붙여 객체 이름으로 출력하는 단일 메서드
"Custom.name()"을 정의합니다.

   static PyObject *
   Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
   {
       if (self->first == NULL) {
           PyErr_SetString(PyExc_AttributeError, "first");
           return NULL;
       }
       if (self->last == NULL) {
           PyErr_SetString(PyExc_AttributeError, "last");
           return NULL;
       }
       return PyUnicode_FromFormat("%S %S", self->first, self->last);
   }

이 메서드는 "Custom" (또는 "Custom" 서브 클래스) 인스턴스를 첫 번째 인
자로 취하는 C 함수로 구현됩니다. 메서드는 항상 인스턴스를 첫 번째 인자
로 취합니다. 메서드는 종종 위치와 키워드 인자도 취하지만, 이 경우에는
아무것도 취하지 않아서 위치 인자 튜플이나 키워드 인자 딕셔너리를 받아
들일 필요 없습니다. 이 메서드는 다음과 같은 파이썬 메서드와 동등합니다
:

   def name(self):
       return "%s %s" % (self.first, self.last)

"first"와 "last" 멤버가 "NULL"일 가능성을 확인해야 함에 유의하십시오.
삭제할 수 있기 때문인데, 이때 "NULL"로 설정됩니다. 이러한 어트리뷰트의
삭제를 방지하고 어트리뷰트 값을 문자열로 제한하는 것이 더 좋습니다. 다
음 섹션에서 이를 수행하는 방법을 살펴보겠습니다.

이제 메서드를 정의했습니다, 메서드 정의 배열을 만들어야 합니다:

   static PyMethodDef Custom_methods[] = {
       {"name", (PyCFunction) Custom_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

(우리는 "METH_NOARGS" 플래그를 사용하여 메서드가 *self* 이외의 인자를
기대하지 않음을 나타냈음에 유의하십시오)

그리고 "tp_methods" 슬롯에 대입합니다:

   .tp_methods = Custom_methods,

마지막으로, 우리의 형을 서브 클래싱의 베이스 클래스로 사용할 수 있게
만들 것입니다. 우리는 지금까지 만들어지거나 사용되고 있는 객체의 형에
대해 가정을 하지 않도록 메서드를 주의해서 작성했습니다, 그래서 클래스
플래그 정의에 "Py_TPFLAGS_BASETYPE"을 추가하기만 하면 됩니다.

   .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,

"PyInit_custom()"의 이름을 "PyInit_custom2()"로 바꾸고, "PyModuleDef"
구조체에서 모듈 이름을 갱신하고, "PyTypeObject" 구조체에서 전체 클래스
이름을 갱신합니다.

마지막으로, 새 모듈을 빌드하기 위해 "setup.py" 파일을 갱신합니다:

   from distutils.core import setup, Extension
   setup(name="custom", version="1.0",
         ext_modules=[
            Extension("custom", ["custom.c"]),
            Extension("custom2", ["custom2.c"]),
            ])


2.3. 데이터 어트리뷰트를 더 세밀하게 제어하기
=============================================

이 섹션에서는, "Custom" 예제에서 "first"와 "last" 어트리뷰트가 설정되
는 방식을 더 세밀하게 제어합니다. 이전 버전의 모듈에서는, 인스턴스 변
수 "first"와 "last"를 문자열이 아닌 값으로 설정하거나 삭제할 수도 있습
니다. 이 어트리뷰트들에 항상 문자열이 포함되도록 하고 싶습니다.

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>
   #include "structmember.h"

   typedef struct {
       PyObject_HEAD
       PyObject *first; /* first name */
       PyObject *last;  /* last name */
       int number;
   } CustomObject;

   static void
   Custom_dealloc(CustomObject *self)
   {
       Py_XDECREF(self->first);
       Py_XDECREF(self->last);
       Py_TYPE(self)->tp_free((PyObject *) self);
   }

   static PyObject *
   Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       CustomObject *self;
       self = (CustomObject *) type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyUnicode_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->last = PyUnicode_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->number = 0;
       }
       return (PyObject *) self;
   }

   static int
   Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
   {
       static char *kwlist[] = {"first", "last", "number", NULL};
       PyObject *first = NULL, *last = NULL, *tmp;

       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                        &first, &last,
                                        &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_DECREF(tmp);
       }
       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_DECREF(tmp);
       }
       return 0;
   }

   static PyMemberDef Custom_members[] = {
       {"number", T_INT, offsetof(CustomObject, number), 0,
        "custom number"},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Custom_getfirst(CustomObject *self, void *closure)
   {
       Py_INCREF(self->first);
       return self->first;
   }

   static int
   Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
   {
       PyObject *tmp;
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
           return -1;
       }
       if (!PyUnicode_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The first attribute value must be a string");
           return -1;
       }
       tmp = self->first;
       Py_INCREF(value);
       self->first = value;
       Py_DECREF(tmp);
       return 0;
   }

   static PyObject *
   Custom_getlast(CustomObject *self, void *closure)
   {
       Py_INCREF(self->last);
       return self->last;
   }

   static int
   Custom_setlast(CustomObject *self, PyObject *value, void *closure)
   {
       PyObject *tmp;
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
           return -1;
       }
       if (!PyUnicode_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The last attribute value must be a string");
           return -1;
       }
       tmp = self->last;
       Py_INCREF(value);
       self->last = value;
       Py_DECREF(tmp);
       return 0;
   }

   static PyGetSetDef Custom_getsetters[] = {
       {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
        "first name", NULL},
       {"last", (getter) Custom_getlast, (setter) Custom_setlast,
        "last name", NULL},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
   {
       return PyUnicode_FromFormat("%S %S", self->first, self->last);
   }

   static PyMethodDef Custom_methods[] = {
       {"name", (PyCFunction) Custom_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

   static PyTypeObject CustomType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       .tp_name = "custom3.Custom",
       .tp_doc = PyDoc_STR("Custom objects"),
       .tp_basicsize = sizeof(CustomObject),
       .tp_itemsize = 0,
       .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
       .tp_new = Custom_new,
       .tp_init = (initproc) Custom_init,
       .tp_dealloc = (destructor) Custom_dealloc,
       .tp_members = Custom_members,
       .tp_methods = Custom_methods,
       .tp_getset = Custom_getsetters,
   };

   static PyModuleDef custommodule = {
       PyModuleDef_HEAD_INIT,
       .m_name = "custom3",
       .m_doc = "Example module that creates an extension type.",
       .m_size = -1,
   };

   PyMODINIT_FUNC
   PyInit_custom3(void)
   {
       PyObject *m;
       if (PyType_Ready(&CustomType) < 0)
           return NULL;

       m = PyModule_Create(&custommodule);
       if (m == NULL)
           return NULL;

       Py_INCREF(&CustomType);
       if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
           Py_DECREF(&CustomType);
           Py_DECREF(m);
           return NULL;
       }

       return m;
   }

"first"와 "last" 어트리뷰트를 더 효과적으로 제어하기 위해, 사용자 정의
게터(getter)와 세터(setter) 함수를 사용합니다. "first" 어트리뷰트를 가
져오고 설정하는 함수는 다음과 같습니다:

   static PyObject *
   Custom_getfirst(CustomObject *self, void *closure)
   {
       Py_INCREF(self->first);
       return self->first;
   }

   static int
   Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
   {
       PyObject *tmp;
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
           return -1;
       }
       if (!PyUnicode_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The first attribute value must be a string");
           return -1;
       }
       tmp = self->first;
       Py_INCREF(value);
       self->first = value;
       Py_DECREF(tmp);
       return 0;
   }

게터(getter) 함수에는 "Custom" 객체와 "클로저(closure)"(void 포인터)가
전달됩니다. 이 경우, 클로저는 무시됩니다. (클로저는 정의 데이터가 게터
(getter)와 세터(setter)로 전달되는 고급 사용법을 지원합니다. 예를 들어
, 클로저의 데이터에 기반하여 가져오거나 설정할 어트리뷰트를 결정하는
단일 게터(getter)와 세터(setter) 함수 집합을 가능하게 합니다.)

세터(setter) 함수에는 "Custom" 객체, 새 값 및 클로저(closure)가 전달됩
니다. 새 값은 "NULL"일 수 있으며, 이 경우 어트리뷰트가 삭제됩니다. 세
터(setter)에서, 우리는 어트리뷰트가 삭제되거나 새 값이 문자열이 아니면
에러를 발생시킵니다.

"PyGetSetDef" 구조체의 배열을 만듭니다:

   static PyGetSetDef Custom_getsetters[] = {
       {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
        "first name", NULL},
       {"last", (getter) Custom_getlast, (setter) Custom_setlast,
        "last name", NULL},
       {NULL}  /* Sentinel */
   };

그리고 "tp_getset" 슬롯에 등록합니다:

   .tp_getset = Custom_getsetters,

"PyGetSetDef" 구조체의 마지막 항목은 위에서 언급한 "클로저"입니다. 이
경우, 클로저를 사용하지 않아서, "NULL"만 전달합니다.

또한 이러한 어트리뷰트에 대한 멤버 정의를 제거합니다:

   static PyMemberDef Custom_members[] = {
       {"number", T_INT, offsetof(CustomObject, number), 0,
        "custom number"},
       {NULL}  /* Sentinel */
   };

또한 문자열만 전달되도록 [3] "tp_init" 처리기를 갱신해야 합니다:

   static int
   Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
   {
       static char *kwlist[] = {"first", "last", "number", NULL};
       PyObject *first = NULL, *last = NULL, *tmp;

       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                        &first, &last,
                                        &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_DECREF(tmp);
       }
       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_DECREF(tmp);
       }
       return 0;
   }

이러한 변경을 통해, "first"와 "last" 멤버가 절대 "NULL"이 아니라고 확
신할 수 있어서, 거의 모든 경우에 "NULL" 값 검사를 제거할 수 있습니다.
이것은 대부분의 "Py_XDECREF()" 호출이 "Py_DECREF()" 호출로 변환될 수
있음을 의미합니다. 이러한 호출을 변경할 수 없는 유일한 장소는
"tp_dealloc" 구현에서인데, "tp_new"에서 이 멤버의 초기화가 실패했을 가
능성이 있습니다.

또한 이전과 마찬가지로, 초기화 함수에서 모듈 초기화 함수와 모듈 이름을
바꾸고, "setup.py" 파일에 추가 정의를 추가합니다.


2.4. 순환 가비지 수거 지원하기
==============================

파이썬에는 참조 횟수가 0이 아닐 때도 불필요한 객체를 식별할 수 있는 *
순환 가비지 수거기 (GC)*가 있습니다. 이것은 객체가 순환에 참여할 때 일
어날 수 있습니다. 예를 들어, 다음을 고려하십시오:

   >>> l = []
   >>> l.append(l)
   >>> del l

이 예에서, 자신을 포함하는 리스트를 만듭니다. 삭제해도 여전히 자체 참
조가 있습니다. 참조 횟수가 0으로 떨어지지 않습니다. 다행스럽게도, 파이
썬의 순환 가비지 수거기는 결국 리스트가 가비지임을 확인하고 해제합니다
.

"Custom" 예제의 두 번째 버전에서는, 모든 종류의 객체를 "first"나
"last" 어트리뷰트에 저장할 수 있었습니다 [4]. 게다가 두 번째와 세 번째
버전에서는, "Custom"을 서브 클래싱 할 수 있었고, 서브 클래스는 임의의
어트리뷰트를 추가할 수 있습니다. 이 두 가지 이유 중 어느 것으로도,
"Custom" 객체는 순환에 참여할 수 있습니다:

   >>> import custom3
   >>> class Derived(custom3.Custom): pass
   ...
   >>> n = Derived()
   >>> n.some_attribute = n

순환 GC가 참조 순환에 참여하는 "Custom" 인스턴스를 올바르게 감지하고
수집할 수 있도록, "Custom" 형은 두 개의 추가 슬롯을 채우고 이러한 슬롯
을 활성화하는 플래그를 활성화해야 합니다:

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>
   #include "structmember.h"

   typedef struct {
       PyObject_HEAD
       PyObject *first; /* first name */
       PyObject *last;  /* last name */
       int number;
   } CustomObject;

   static int
   Custom_traverse(CustomObject *self, visitproc visit, void *arg)
   {
       Py_VISIT(self->first);
       Py_VISIT(self->last);
       return 0;
   }

   static int
   Custom_clear(CustomObject *self)
   {
       Py_CLEAR(self->first);
       Py_CLEAR(self->last);
       return 0;
   }

   static void
   Custom_dealloc(CustomObject *self)
   {
       PyObject_GC_UnTrack(self);
       Custom_clear(self);
       Py_TYPE(self)->tp_free((PyObject *) self);
   }

   static PyObject *
   Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   {
       CustomObject *self;
       self = (CustomObject *) type->tp_alloc(type, 0);
       if (self != NULL) {
           self->first = PyUnicode_FromString("");
           if (self->first == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->last = PyUnicode_FromString("");
           if (self->last == NULL) {
               Py_DECREF(self);
               return NULL;
           }
           self->number = 0;
       }
       return (PyObject *) self;
   }

   static int
   Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
   {
       static char *kwlist[] = {"first", "last", "number", NULL};
       PyObject *first = NULL, *last = NULL, *tmp;

       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                        &first, &last,
                                        &self->number))
           return -1;

       if (first) {
           tmp = self->first;
           Py_INCREF(first);
           self->first = first;
           Py_DECREF(tmp);
       }
       if (last) {
           tmp = self->last;
           Py_INCREF(last);
           self->last = last;
           Py_DECREF(tmp);
       }
       return 0;
   }

   static PyMemberDef Custom_members[] = {
       {"number", T_INT, offsetof(CustomObject, number), 0,
        "custom number"},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Custom_getfirst(CustomObject *self, void *closure)
   {
       Py_INCREF(self->first);
       return self->first;
   }

   static int
   Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
   {
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
           return -1;
       }
       if (!PyUnicode_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The first attribute value must be a string");
           return -1;
       }
       Py_INCREF(value);
       Py_CLEAR(self->first);
       self->first = value;
       return 0;
   }

   static PyObject *
   Custom_getlast(CustomObject *self, void *closure)
   {
       Py_INCREF(self->last);
       return self->last;
   }

   static int
   Custom_setlast(CustomObject *self, PyObject *value, void *closure)
   {
       if (value == NULL) {
           PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
           return -1;
       }
       if (!PyUnicode_Check(value)) {
           PyErr_SetString(PyExc_TypeError,
                           "The last attribute value must be a string");
           return -1;
       }
       Py_INCREF(value);
       Py_CLEAR(self->last);
       self->last = value;
       return 0;
   }

   static PyGetSetDef Custom_getsetters[] = {
       {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
        "first name", NULL},
       {"last", (getter) Custom_getlast, (setter) Custom_setlast,
        "last name", NULL},
       {NULL}  /* Sentinel */
   };

   static PyObject *
   Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
   {
       return PyUnicode_FromFormat("%S %S", self->first, self->last);
   }

   static PyMethodDef Custom_methods[] = {
       {"name", (PyCFunction) Custom_name, METH_NOARGS,
        "Return the name, combining the first and last name"
       },
       {NULL}  /* Sentinel */
   };

   static PyTypeObject CustomType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       .tp_name = "custom4.Custom",
       .tp_doc = PyDoc_STR("Custom objects"),
       .tp_basicsize = sizeof(CustomObject),
       .tp_itemsize = 0,
       .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
       .tp_new = Custom_new,
       .tp_init = (initproc) Custom_init,
       .tp_dealloc = (destructor) Custom_dealloc,
       .tp_traverse = (traverseproc) Custom_traverse,
       .tp_clear = (inquiry) Custom_clear,
       .tp_members = Custom_members,
       .tp_methods = Custom_methods,
       .tp_getset = Custom_getsetters,
   };

   static PyModuleDef custommodule = {
       PyModuleDef_HEAD_INIT,
       .m_name = "custom4",
       .m_doc = "Example module that creates an extension type.",
       .m_size = -1,
   };

   PyMODINIT_FUNC
   PyInit_custom4(void)
   {
       PyObject *m;
       if (PyType_Ready(&CustomType) < 0)
           return NULL;

       m = PyModule_Create(&custommodule);
       if (m == NULL)
           return NULL;

       Py_INCREF(&CustomType);
       if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
           Py_DECREF(&CustomType);
           Py_DECREF(m);
           return NULL;
       }

       return m;
   }

첫째, 탐색(traversal) 메서드는 순환 GC가 순환에 참여할 수 있는 서브 객
체에 대해 알 수 있도록 합니다:

   static int
   Custom_traverse(CustomObject *self, visitproc visit, void *arg)
   {
       int vret;
       if (self->first) {
           vret = visit(self->first, arg);
           if (vret != 0)
               return vret;
       }
       if (self->last) {
           vret = visit(self->last, arg);
           if (vret != 0)
               return vret;
       }
       return 0;
   }

순환에 참여할 수 있는 각 서브 객체에 대해 탐색 메서드에 전달되는
"visit()" 함수를 호출해야 합니다. "visit()" 함수는 서브 객체와 탐색 메
서드에 전달한 추가 인자 *arg* 인자를 인자로 취합니다. 0이 아닌 경우 반
환해야 하는 정숫값을 반환합니다.

파이썬은 visit 함수 호출을 자동화하는 "Py_VISIT()" 매크로를 제공합니다
. "Py_VISIT()"를 사용하면, "Custom_traverse"에서 관용구 양을 최소화할
수 있습니다:

   static int
   Custom_traverse(CustomObject *self, visitproc visit, void *arg)
   {
       Py_VISIT(self->first);
       Py_VISIT(self->last);
       return 0;
   }

참고:

  "Py_VISIT()"를 사용하려면 "tp_traverse" 구현에서 인자 이름을 *visit*
  과 *arg*로 정확하게 지정해야 합니다.

둘째, 순환에 참여할 수 있는 서브 객체를 지우는 메서드를 제공해야 합니
다:

   static int
   Custom_clear(CustomObject *self)
   {
       Py_CLEAR(self->first);
       Py_CLEAR(self->last);
       return 0;
   }

"Py_CLEAR()" 매크로 사용에 주목하십시오. 참조 횟수를 줄이면서 임의 형
의 데이터 어트리뷰트를 지우는 권장되고 안전한 방법입니다. "NULL"로 설
정하기 전에 어트리뷰트에서 "Py_XDECREF()"를 대신 호출했으면, 어트리뷰
트의 파괴자가 어트리뷰트를 다시 읽는 코드(*특히* 참조 순환이 있으면 )
를 다시 호출할 가능성이 있습니다.

참고:

  다음과 같이 작성하여 "Py_CLEAR()"를 에뮬레이션할 수 있습니다:

     PyObject *tmp;
     tmp = self->first;
     self->first = NULL;
     Py_XDECREF(tmp);

  그런데도, 어트리뷰트를 삭제할 때 항상 "Py_CLEAR()"를 사용하기가 훨씬
  쉽고 에러가 적습니다. 견고성을 희생하면서 세밀한 최적화를 시도하지
  마십시오!

할당 해제기 "Custom_dealloc"은 어트리뷰트를 지울 때 임의의 코드를 호출
할 수 있습니다. 이는 함수 내에서 순환 GC가 트리거 될 수 있음을 의미합
니다. GC는 참조 횟수가 0이 아니라고 가정하기 때문에, 멤버를 지우기 전
에 "PyObject_GC_UnTrack()"을 호출하여 GC에서 객체를 추적 해제해야 합니
다. 다음은 "PyObject_GC_UnTrack()"과 "Custom_clear"를 사용하여 다시 구
현된 할당 해제기입니다:

   static void
   Custom_dealloc(CustomObject *self)
   {
       PyObject_GC_UnTrack(self);
       Custom_clear(self);
       Py_TYPE(self)->tp_free((PyObject *) self);
   }

마지막으로, "Py_TPFLAGS_HAVE_GC" 플래그를 클래스 플래그에 추가합니다:

   .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,

거의 다 됐습니다. 사용자 정의 "tp_alloc"이나 "tp_free" 처리기를 작성했
으면, 순환 가비지 수거를 위해 이를 수정해야 합니다. 대부분의 확장은 자
동으로 제공된 버전을 사용합니다.


2.5. 다른 형의 서브 클래싱
==========================

기존 형에서 파생된 새 확장형을 만들 수 있습니다. 확장이 필요한
"PyTypeObject"를 쉽게 사용할 수 있어서, 내장형에서 상속하기가 가장 쉽
습니다. 확장 모듈 간에 이러한 "PyTypeObject" 구조체를 공유하기 어려울
수 있습니다.

이 예에서는 내장 "list" 형을 상속하는 "SubList" 형을 만듭니다. 새로운
형은 일반 리스트와 완전히 호환되지만, 내부 카운터를 증가시키는 추가
"increment()" 메서드가 있습니다:

   >>> import sublist
   >>> s = sublist.SubList(range(3))
   >>> s.extend(s)
   >>> print(len(s))
   6
   >>> print(s.increment())
   1
   >>> print(s.increment())
   2

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>

   typedef struct {
       PyListObject list;
       int state;
   } SubListObject;

   static PyObject *
   SubList_increment(SubListObject *self, PyObject *unused)
   {
       self->state++;
       return PyLong_FromLong(self->state);
   }

   static PyMethodDef SubList_methods[] = {
       {"increment", (PyCFunction) SubList_increment, METH_NOARGS,
        PyDoc_STR("increment state counter")},
       {NULL},
   };

   static int
   SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
   {
       if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
           return -1;
       self->state = 0;
       return 0;
   }

   static PyTypeObject SubListType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       .tp_name = "sublist.SubList",
       .tp_doc = PyDoc_STR("SubList objects"),
       .tp_basicsize = sizeof(SubListObject),
       .tp_itemsize = 0,
       .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
       .tp_init = (initproc) SubList_init,
       .tp_methods = SubList_methods,
   };

   static PyModuleDef sublistmodule = {
       PyModuleDef_HEAD_INIT,
       .m_name = "sublist",
       .m_doc = "Example module that creates an extension type.",
       .m_size = -1,
   };

   PyMODINIT_FUNC
   PyInit_sublist(void)
   {
       PyObject *m;
       SubListType.tp_base = &PyList_Type;
       if (PyType_Ready(&SubListType) < 0)
           return NULL;

       m = PyModule_Create(&sublistmodule);
       if (m == NULL)
           return NULL;

       Py_INCREF(&SubListType);
       if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
           Py_DECREF(&SubListType);
           Py_DECREF(m);
           return NULL;
       }

       return m;
   }

보시다시피, 소스 코드는 이전 섹션의 "Custom" 예제와 매우 유사합니다.
우리는 그들 사이의 주요 차이점을 분석할 것입니다.

   typedef struct {
       PyListObject list;
       int state;
   } SubListObject;

파생형 객체의 주요 차이점은 베이스형의 객체 구조체가 첫 번째 값이어야
한다는 것입니다. 베이스형은 이미 구조체의 시작 부분에
"PyObject_HEAD()"를 포함합니다.

파이썬 객체가 "SubList" 인스턴스일 때, "PyObject *" 포인터는
"PyListObject *"와 "SubListObject *" 모두로 안전하게 캐스팅될 수 있습
니다:

   static int
   SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
   {
       if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
           return -1;
       self->state = 0;
       return 0;
   }

위에서 베이스형의 "__init__" 메서드를 호출하는 방법을 볼 수 있습니다.

이 패턴은 사용자 정의 "tp_new"와 "tp_dealloc" 멤버를 갖는 형을 작성할
때 중요합니다. "tp_new" 처리기는 실제로 "tp_alloc"을 사용하여 객체의
메모리를 만들지 말고, 베이스 클래스가 자체 "tp_new"를 호출하여 처리하
도록 해야 합니다.

"PyTypeObject" 구조체는 형의 구상 베이스 클래스를 지정하는 "tp_base"를
지원합니다. 크로스 플랫폼 컴파일러 문제로 인해, "PyList_Type"에 대한
참조로 해당 필드를 직접 채울 수 없습니다; 나중에 모듈 초기화 함수에서
수행해야 합니다:

   PyMODINIT_FUNC
   PyInit_sublist(void)
   {
       PyObject* m;
       SubListType.tp_base = &PyList_Type;
       if (PyType_Ready(&SubListType) < 0)
           return NULL;

       m = PyModule_Create(&sublistmodule);
       if (m == NULL)
           return NULL;

       Py_INCREF(&SubListType);
       if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
           Py_DECREF(&SubListType);
           Py_DECREF(m);
           return NULL;
       }

       return m;
   }

"PyType_Ready()"를 호출하기 전에, 형 구조체에 "tp_base" 슬롯이 채워져
있어야 합니다. 기존 형을 파생할 때, "PyType_GenericNew()"로 "tp_alloc"
슬롯을 채울 필요는 없습니다 -- 베이스형의 할당 함수가 상속됩니다.

그런 다음, "PyType_Ready()"를 호출하고 형 객체를 모듈에 추가하는 것은
기초 "Custom" 예와 같습니다.

-[ 각주 ]-

[1] 이것은 객체가 문자열이나 부동 소수점과 같은 기본형이라는 것을 알고
    있을 때 참입니다.

[2] 우리의 형이 가비지 수거를 지원하지 않기 때문에, 이 예제에서는
    "tp_dealloc" 처리기에서 이것을 사용했습니다.

[3] 우리는 이제 first와 last 멤버가 문자열이라는 것을 알고 있어서, 참
    조 횟수를 줄이는 데 덜 주의할 수 있지만, 우리는 문자열 서브 클래스
    의 인스턴스를 받아들입니다. 일반 문자열을 할당 해제하는 것이 우리
    객체로 다시 호출되지는 않더라도, 문자열 서브 클래스의 인스턴스를
    할당 해제하는 것이 객체로 다시 호출되지 않는다고 보장할 수 없습니
    다.

[4] 또한, 어트리뷰트가 문자열 인스턴스로 제한된 경우에도, 사용자는 임
    의의 "str" 서브 클래스를 전달할 수 있어서 여전히 참조 순환을 만들
    수 있습니다.
