3. 확장형 정의하기: 여러 가지 주제
**********************************

이 섹션은 구현할 수 있는 다양한 형 메서드와 그것이 하는 일에 대해 훑어
보기를 제공하기 위한 것입니다.

다음은 디버그 빌드에서만 사용되는 일부 필드가 생략된 "PyTypeObject"의
정의입니다:

   typedef struct _typeobject {
       PyObject_VAR_HEAD
       const char *tp_name; /* 인쇄용, "<module>.<name>" 형식
       Py_ssize_t tp_basicsize, tp_itemsize; /* 할당(allocation)용 */

       /* 표준 연산을 구현하는 메서드 */

       destructor tp_dealloc;
       Py_ssize_t tp_vectorcall_offset;
       getattrfunc tp_getattr;
       setattrfunc tp_setattr;
       PyAsyncMethods *tp_as_async; /* 이전에는 tp_compare (파이썬 2)나
                                       tp_reserved (파이썬 3) 로 알려졌습니다 */
       reprfunc tp_repr;

       /* 표준 클래스를 위한 메서드 스위트 */

       PyNumberMethods *tp_as_number;
       PySequenceMethods *tp_as_sequence;
       PyMappingMethods *tp_as_mapping;

       /* 더 많은 표준 연산 (바이너리 호환성을 위해 여기에) */

       hashfunc tp_hash;
       ternaryfunc tp_call;
       reprfunc tp_str;
       getattrofunc tp_getattro;
       setattrofunc tp_setattro;

       /* 입/출력 버퍼로 객체를 액세스하는 함수 */
       PyBufferProcs *tp_as_buffer;

       /* 선택/확장 기능의 존재를 정의하는 플래그 */
       unsigned long tp_flags;

       const char *tp_doc; /* 설명서 문자열 */

       /* 릴리스 2.0에서 할당된 의미 */
       /* 모든 액세스 가능한 객체에 대한 호출 함수 */
       traverseproc tp_traverse;

       /* 포함된 객체에 대한 참조 삭제 */
       inquiry tp_clear;

       /* 릴리스 2.1에서 할당된 의미 */
       /* 풍부한 비교 */
       richcmpfunc tp_richcompare;

       /* 약한 참조 활성화기 */
       Py_ssize_t tp_weaklistoffset;

       /* 이터레이터 */
       getiterfunc tp_iter;
       iternextfunc tp_iternext;

       /* 어트리뷰트 디스크립터와 서브 클래싱 */
       struct PyMethodDef *tp_methods;
       struct PyMemberDef *tp_members;
       struct PyGetSetDef *tp_getset;
       // 힙 형에는 강한 참조, 정적 형에는 빌려온 참조
       struct _typeobject *tp_base;
       PyObject *tp_dict;
       descrgetfunc tp_descr_get;
       descrsetfunc tp_descr_set;
       Py_ssize_t tp_dictoffset;
       initproc tp_init;
       allocfunc tp_alloc;
       newfunc tp_new;
       freefunc tp_free; /* 저수준의 메모리 해제 루틴 */
       inquiry tp_is_gc; /* PyObject_IS_GC 용 */
       PyObject *tp_bases;
       PyObject *tp_mro; /* 메서드 결정 순서 */
       PyObject *tp_cache;
       PyObject *tp_subclasses;
       PyObject *tp_weaklist;
       destructor tp_del;

       /* 형 어트리뷰트 캐시 버전 태그. 버전 2.6에서 추가되었습니다 */
       unsigned int tp_version_tag;

       destructor tp_finalize;
       vectorcallfunc tp_vectorcall;

       /* 이 형을 신경 쓰는 형 감시자의 비트 세트 */
       unsigned char tp_watched;
   } PyTypeObject;

이제 메서드가 아주 *많습니다*. 너무 걱정하지 마십시오 -- 정의하려는 형
이 있으면, 이 중 일부만 구현할 가능성이 매우 높습니다.

아마 지금까지 예상했듯이, 이것에 대해 살펴보고 다양한 처리기에 대한 자
세한 정보를 제공할 것입니다. 필드의 순서에 영향을 미치는 많은 과거의
짐이 있어서, 구조체에 정의된 순서대로 진행하지 않을 것입니다. 필요한
필드가 포함된 예제를 찾은 다음 새 형에 맞게 값을 변경하기가 종종 가장
쉽습니다.

   const char *tp_name; /* 인쇄 용 */

형의 이름 -- 이전 장에서 언급했듯이, 이것은 여러 곳에서 나타나는데, 거
의 진단 목적입니다. 그러한 상황에서 도움이 될만한 것을 선택하십시오!

   Py_ssize_t tp_basicsize, tp_itemsize; /* 할당(allocation)용 */

이 필드는 이 형의 새 객체가 만들어질 때 할당할 메모리양을 런타임에 알
려줍니다. 파이썬은 가변 길이 구조(생각하세요: 문자열, 튜플)에 대한 지
원을 내장하고 있는데, 이때 "tp_itemsize" 필드가 참여합니다. 이것은 나
중에 다룰 것입니다.

   const char *tp_doc;

여기에 파이썬 스크립트가 "obj.__doc__"을 참조하여 독스트링을 꺼낼 때
반환할 문자열(또는 문자열의 주소)을 넣을 수 있습니다.

이제 기본 형 메서드에 대해 살펴보겠습니다 -- 대부분의 확장형이 구현할
것들입니다.


3.1. 파이널리제이션과 할당 해제
===============================

   destructor tp_dealloc;

이 함수는 형의 인스턴스의 참조 횟수가 0으로 줄어들고 파이썬 인터프리터
가 그것을 재활용하고자 할 때 호출됩니다. 여러분의 형에 해제할 메모리가
있거나 수행할 기타 정리 작업이 있으면, 여기에 넣을 수 있습니다. 객체
자체도 여기서 해제해야 합니다. 이 함수의 예는 다음과 같습니다:

   static void
   newdatatype_dealloc(newdatatypeobject *obj)
   {
       free(obj->obj_UnderlyingDatatypePtr);
       Py_TYPE(obj)->tp_free((PyObject *)obj);
   }

형이 가비지 수거를 지원하면, 파괴자는 멤버 필드를 지우기 전에
"PyObject_GC_UnTrack()"을 호출해야 합니다:

   static void
   newdatatype_dealloc(newdatatypeobject *obj)
   {
       PyObject_GC_UnTrack(obj);
       Py_CLEAR(obj->other_obj);
       ...
       Py_TYPE(obj)->tp_free((PyObject *)obj);
   }

할당 해제 함수의 중요한 요구 사항 중 하나는 계류 중인 예외를 그대로 남
겨 두어야 한다는 것입니다. 인터프리터가 파이썬 스택을 되감을 때 할당
해제기가 자주 호출되기 때문에 중요합니다; 스택이 (정상적인 반환이 아닌
) 예외로 인해 되감길 때, 할당 해제기가 예외가 이미 설정되어 있음을 알
수 없도록 하는 것은 아무것도 수행되지 않습니다. 할당 해제기가 수행하는
추가 파이썬 코드가 실행될 수 있도록 하는 추가 조치는 예외가 설정되었음
을 감지할 수 있습니다. 이는 인터프리터가 혼동하도록 할 수 있습니다. 이
를 방지하는 올바른 방법은 안전하지 않은 조치를 수행하기 전에 계류 중인
예외를 저장하고 완료되면 복원하는 것입니다. "PyErr_Fetch()"와
"PyErr_Restore()" 함수를 사용하여 수행할 수 있습니다:

   static void
   my_dealloc(PyObject *obj)
   {
       MyObject *self = (MyObject *) obj;
       PyObject *cbresult;

       if (self->my_callback != NULL) {
           PyObject *err_type, *err_value, *err_traceback;

           /* 현재 예외 상태를 저장합니다 */
           PyErr_Fetch(&err_type, &err_value, &err_traceback);

           cbresult = PyObject_CallNoArgs(self->my_callback);
           if (cbresult == NULL)
               PyErr_WriteUnraisable(self->my_callback);
           else
               Py_DECREF(cbresult);

           /* 저장된 예외 상태를 복원합니다 */
           PyErr_Restore(err_type, err_value, err_traceback);

           Py_DECREF(self->my_callback);
       }
       Py_TYPE(obj)->tp_free((PyObject*)self);
   }

참고:

  할당 해제 함수에서 안전하게 수행할 수 있는 작업에는 제한이 있습니다.
  먼저, 형이 가비지 수거를 지원하면 ("tp_traverse" 및/또는 "tp_clear"
  를 사용해서), "tp_dealloc"이 호출될 때 객체의 일부 멤버가 지워지거나
  파이널라이즈 될 수 있습니다. 둘째, "tp_dealloc"에서, 객체는 불안정한
  상태에 있습니다: 참조 횟수가 0입니다. (위의 예에서와같이) 사소하지
  않은 객체나 API를 호출하면 "tp_dealloc"을 다시 호출하게 되어, 이중
  해제와 충돌이 발생할 수 있습니다.파이썬 3.4부터는, "tp_dealloc"에 복
  잡한 파이널리제이션 코드를 넣지 말고, 대신 새로운 "tp_finalize" 형
  메서드를 사용하는 것이 좋습니다.

  더 보기: **PEP 442**는 새로운 파이널리제이션 체계를 설명합니다.


3.2. 객체 표현
==============

파이썬에서, 객체의 텍스트 표현을 생성하는 두 가지 방법이 있습니다:
"repr()" 함수와 "str()" 함수. ("print()" 함수는 단지 "str()"을 호출합
니다.) 이 처리기들은 모두 선택적입니다.

   reprfunc tp_repr;
   reprfunc tp_str;

"tp_repr" 처리기는 호출된 인스턴스의 표현을 포함하는 문자열 객체를 반
환해야 합니다. 다음은 간단한 예입니다:

   static PyObject *
   newdatatype_repr(newdatatypeobject *obj)
   {
       return PyUnicode_FromFormat("Repr-ified_newdatatype{{size:%d}}",
                                   obj->obj_UnderlyingDatatypePtr->size);
   }

"tp_repr" 처리기가 지정되지 않으면, 인터프리터는 형의 "tp_name"과 객체
의 고유 식별 값을 사용하는 표현을 제공합니다.

"tp_str" 처리기는 "str()"에 대한 것이고, 위에서 설명한 "tp_repr" 처리
기와 "repr()" 간의 관계와 같은 관계입니다; 즉, 파이썬 코드가 객체의 인
스턴스에서 "str()"을 호출할 때 호출됩니다. 구현은 "tp_repr" 함수와 매
우 유사하지만, 결과 문자열은 사람이 사용하기 위한 것입니다. "tp_str"을
지정하지 않으면, "tp_repr" 처리기가 대신 사용됩니다.

다음은 간단한 예입니다:

   static PyObject *
   newdatatype_str(newdatatypeobject *obj)
   {
       return PyUnicode_FromFormat("Stringified_newdatatype{{size:%d}}",
                                   obj->obj_UnderlyingDatatypePtr->size);
   }


3.3. 어트리뷰트 관리
====================

어트리뷰트를 지원할 수 있는 모든 객체에 대해, 해당 형은 어트리뷰트가
결정되는(resolved) 방법을 제어하는 함수를 제공해야 합니다. 어트리뷰트
를 꺼낼 수 있는 함수와 (뭔가 정의되어 있다면), 어트리뷰트를 설정하는
다른 함수(어트리뷰트 설정이 허용된다면)가 있어야 합니다. 어트리뷰트 제
거는 특별한 경우이며, 처리기에 전달된 새 값이 "NULL"입니다.

파이썬은 두 쌍의 어트리뷰트 처리기를 지원합니다; 어트리뷰트를 지원하는
형은 한 쌍의 함수만 구현하면 됩니다. 차이점은 한 쌍은 어트리뷰트 이름
을 char*로 취하고, 다른 쌍은 PyObject*를 받아들인다는 것입니다. 각 형
은 구현의 편의에 더 적합한 쌍을 사용할 수 있습니다.

   getattrfunc  tp_getattr;        /* char * 버전 */
   setattrfunc  tp_setattr;
   /* ... */
   getattrofunc tp_getattro;       /* PyObject * 버전 */
   setattrofunc tp_setattro;

객체의 어트리뷰트에 액세스하는 것이 항상 간단한 연산이면 (짧게 설명할
것입니다), 어트리뷰트 관리 함수의 PyObject* 버전을 제공하는 데 사용할
수 있는 일반적인 구현이 있습니다. 파이썬 2.2부터 형별 어트리뷰트 처리
기에 대한 실제 필요성은 거의 완전히 사라졌지만, 사용 가능한 새로운 일
반 메커니즘을 사용하도록 갱신되지 않은 예제가 많이 있습니다.


3.3.1. 범용 어트리뷰트 관리
---------------------------

대부분의 확장형은 *간단한* 어트리뷰트만 사용합니다. 그렇다면, 어트리뷰
트를 간단하게 만드는 것은 무엇입니까? 충족해야 하는 몇 가지 조건만 있
습니다:

1. "PyType_Ready()"가 호출될 때 어트리뷰트의 이름을 알아야 합니다.

2. 어트리뷰트를 찾거나 설정했음을 기록하는 데 특별한 처리가 필요하지
   않으며 값을 기반으로 조처를 하지 않아도 됩니다.

이 목록은 어트리뷰트 값, 값을 계산하는 시점 또는 관련 데이터가 저장되
는 방법에 제한을 두지 않음에 유의하십시오.

"PyType_Ready()"가 호출될 때, 형 객체가 참조하는 3개의 테이블을 사용하
여 형 객체의 딕셔너리에 배치되는 *디스크립터*를 만듭니다. 각 디스크립
터는 인스턴스 객체의 한 어트리뷰트에 대한 액세스를 제어합니다. 각 테이
블은 선택적입니다; 세 개 모두가 "NULL"이면, 형의 인스턴스는 베이스형에
서 상속된 어트리뷰트만 갖게 되며, "tp_getattro"와 "tp_setattro" 필드도
"NULL"로 남겨두어야 베이스형이 어트리뷰트를 처리할 수 있습니다.

테이블은 형 객체의 세 필드로 선언됩니다:

   struct PyMethodDef *tp_methods;
   struct PyMemberDef *tp_members;
   struct PyGetSetDef *tp_getset;

"tp_methods"가 "NULL"이 아니면, "PyMethodDef" 구조체의 배열을 참조해야
합니다. 테이블의 각 항목은 다음 구조체의 인스턴스입니다:

   typedef struct PyMethodDef {
       const char  *ml_name;       /* 메서드 이름 */
       PyCFunction  ml_meth;       /* 구현 함수 */
       int          ml_flags;      /* 플래그 */
       const char  *ml_doc;        /* 독스트링 */
   } PyMethodDef;

형에서 제공되는 각 메서드에 대해 하나의 항목을 정의해야 합니다; 베이스
형에서 상속된 메서드에는 항목이 필요하지 않습니다. 마지막에 하나의 추
가 항목이 필요합니다; 배열의 끝을 나타내는 센티넬(sentinel)입니다. 센
티넬의 "ml_name" 필드는 "NULL"이어야 합니다.

두 번째 테이블은 인스턴스에 저장된 데이터에 직접 매핑되는 어트리뷰트를
정의하는 데 사용됩니다. 다양한 기본 C형이 지원되며, 액세스는 읽기 전용
이거나 읽고 쓰기일 수 있습니다. 테이블의 구조체는 다음과 같이 정의됩니
다:

   typedef struct PyMemberDef {
       const char *name;
       int         type;
       int         offset;
       int         flags;
       const char *doc;
   } PyMemberDef;

테이블의 각 항목에 대해, *디스크립터*가 구성되고 형에 추가되어 인스턴
스 구조체에서 값을 추출할 수 있게 됩니다. "type" 필드는 "Py_T_INT" 나
"Py_T_DOUBLE"와 같은 형 코드를 포함해야 합니다; 이 값은 파이썬 값과 C
값 간에 변환하는 방법을 결정하는 데 사용됩니다. "flags" 필드는 어트리
뷰트에 액세스하는 방법을 제어하는 플래그를 저장하는 데 사용됩니다: 이
를 "Py_READONLY"로 설정하면 파이썬 코드가 이를 설정하지 못하도록 방지
할 수 있습니다.

"tp_members" 테이블을 사용하여 실행 시간에 사용되는 디스크립터를 구축
하는 것의 흥미로운 이점은 이 방법으로 정의된 모든 어트리뷰트가 단순히
테이블에 텍스트를 제공하는 것으로 연관된 독스트링을 가질 수 있다는 것
입니다. 응용 프로그램은 내부 검사(introspection) API를 사용하여 클래스
객체에서 디스크립터를 꺼내고, 그것의 "__doc__" 어트리뷰트를 사용하여
독스트링을 얻을 수 있습니다.

"tp_methods" 테이블과 마찬가지로, "ml_name" 값이 "NULL"인 센티넬 항목
이 필요합니다.


3.3.2. 형별 어트리뷰트 관리
---------------------------

간단히 하기 위해, char* 버전 만 여기에서 예시합니다; name 매개 변수의
형이 인터페이스의 char*와 PyObject* 버전 간의 유일한 차이점입니다. 이
예제는 위의 범용 예제와 효과적으로 같은 것을 수행하지만, 파이썬 2.2에
추가된 범용 지원은 사용하지 않습니다. 처리기 함수가 호출되는 방식을 설
명하므로, 기능을 확장해야 한다면, 무엇을 해야 할지 이해할 수 있을 겁니
다.

"tp_getattr" 처리기는 객체에 어트리뷰트 조회가 필요할 때 호출됩니다.
클래스의 "__getattr__()" 메서드가 호출되는 것과 같은 상황에서 호출됩니
다.

예는 다음과 같습니다:

   static PyObject *
   newdatatype_getattr(newdatatypeobject *obj, char *name)
   {
       if (strcmp(name, "data") == 0)
       {
           return PyLong_FromLong(obj->data);
       }

       PyErr_Format(PyExc_AttributeError,
                    "'%.100s' object has no attribute '%.400s'",
                    Py_TYPE(obj)->tp_name, name);
       return NULL;
   }

"tp_setattr" 처리기는 클래스 인스턴스의 "__setattr__()"이나
"__delattr__()" 메서드가 호출될 때 호출됩니다. 어트리뷰트를 삭제해야
하면, 세 번째 매개 변수는 "NULL"이 됩니다. 다음은 단순히 예외를 발생시
키는 예입니다; 이것이 정말로 여러분이 원하는 전부라면, "tp_setattr" 처
리기는 "NULL"로 설정되어야 합니다.

   static int
   newdatatype_setattr(newdatatypeobject *obj, char *name, PyObject *v)
   {
       PyErr_Format(PyExc_RuntimeError, "Read-only attribute: %s", name);
       return -1;
   }


3.4. 객체 비교
==============

   richcmpfunc tp_richcompare;

"tp_richcompare" 처리기는 비교가 필요할 때 호출됩니다. "__lt__()"와 같
은 풍부한 비교 메서드에 해당하며, "PyObject_RichCompare()"와
"PyObject_RichCompareBool()"에 의해서도 호출됩니다.

이 함수는 두 개의 파이썬 객체와 연산자를 인자로 사용하여 호출됩니다,
여기서 연산자는 "Py_EQ", "Py_NE", "Py_LE", "Py_GE", "Py_LT" 또는
"Py_GT" 중 하나입니다. 지정된 연산자로 두 객체를 비교하고 비교에 성공
하면 "Py_True"나 "Py_False"를, 비교가 구현되지 않았으며 다른 객체의 비
교 메서드를 시도해야 한다는 것을 나타내려면 "Py_NotImplemented"를, 예
외가 설정되면 "NULL"을 반환해야 합니다.

내부 포인터의 크기가 같으면 같다고 간주하는 데이터형에 대한 샘플 구현
은 다음과 같습니다:

   static PyObject *
   newdatatype_richcmp(newdatatypeobject *obj1, newdatatypeobject *obj2, int op)
   {
       PyObject *result;
       int c, size1, size2;

       /* 두 인자가 모두 newdatatype 형인지 확인하는 코드는
           생략했습니다 */

       size1 = obj1->obj_UnderlyingDatatypePtr->size;
       size2 = obj2->obj_UnderlyingDatatypePtr->size;

       switch (op) {
       case Py_LT: c = size1 <  size2; break;
       case Py_LE: c = size1 <= size2; break;
       case Py_EQ: c = size1 == size2; break;
       case Py_NE: c = size1 != size2; break;
       case Py_GT: c = size1 >  size2; break;
       case Py_GE: c = size1 >= size2; break;
       }
       result = c ? Py_True : Py_False;
       Py_INCREF(result);
       return result;
    }


3.5. 추상 프로토콜 지원
=======================

파이썬은 다양한 *추상* '프로토콜'을 지원합니다; 이러한 인터페이스를 사
용하기 위해 제공되는 구체적인 인터페이스는 추상 객체 계층에 설명되어
있습니다.

이러한 추상 인터페이스 중 다수는 파이썬 구현 개발 초기에 정의되었습니
다. 특히, 숫자, 매핑 및 시퀀스 프로토콜은 처음부터 파이썬의 일부였습니
다. 다른 프로토콜은 시간이 지남에 따라 추가되었습니다. 형 구현의 여러
처리기 루틴에 의존하는 프로토콜의 경우, 이전 프로토콜은 형 객체가 참조
하는 선택적 처리기 블록으로 정의되었습니다. 최신 프로토콜의 경우 메인
형 객체에 추가 슬롯이 있으며, 슬롯이 존재하고 인터프리터가 확인해야 함
을 나타내는 플래그 비트가 설정됩니다. (플래그 비트는 슬롯 값이 "NULL"
이 아님을 나타내지 않습니다. 플래그는 슬롯의 존재를 나타내도록 설정될
수 있지만, 슬롯은 여전히 채워지지 않을 수 있습니다.)

   PyNumberMethods   *tp_as_number;
   PySequenceMethods *tp_as_sequence;
   PyMappingMethods  *tp_as_mapping;

여러분의 객체가 숫자, 시퀀스 또는 매핑 객체처럼 작동하도록 하려면, C형
"PyNumberMethods", "PySequenceMethods" 또는 "PyMappingMethods"를 각각
구현하는 구조체의 주소를 배치합니다. 이 구조체를 적절한 값으로 채우는
것은 여러분의 책임입니다. 파이썬 소스 배포의 "Objects" 디렉터리에서 이
들 각각의 사용 예를 찾을 수 있습니다.

   hashfunc tp_hash;

여러분이 제공하기로 선택했다면, 이 함수는 데이터형의 인스턴스에 대한
해시 숫자를 반환해야 합니다. 다음은 간단한 예입니다:

   static Py_hash_t
   newdatatype_hash(newdatatypeobject *obj)
   {
       Py_hash_t result;
       result = obj->some_size + 32767 * obj->some_number;
       if (result == -1)
          result = -2;
       return result;
   }

"Py_hash_t"는 플랫폼에 따라 변하는 너비의 부호 있는 정수 형입니다.
"tp_hash"에서 "-1"을 반환하면 에러를 표시해서, 위와 같이 해시 계산에
성공했을 때 반환하지 않도록 주의해야 합니다.

   ternaryfunc tp_call;

이 함수는 데이터형의 인스턴스가 "호출"될 때 호출됩니다, 예를 들어,
"obj1"이 데이터형의 인스턴스이고 파이썬 스크립트에 "obj1('hello')"가
포함되어 있으면 "tp_call" 처리기가 호출됩니다.

이 함수는 세 개의 인자를 취합니다:

1. *self*는 호출의 대상인 데이터형의 인스턴스입니다. 호출이
   "obj1('hello')"이면, *self*는 "obj1"입니다.

2. *args*는 호출에 대한 인자를 포함하는 튜플입니다.
   "PyArg_ParseTuple()"을 사용하여 인자를 추출할 수 있습니다.

3. *kwds*는 전달된 키워드 인자의 딕셔너리입니다. 이것이 "NULL"이 아니
   고 키워드 인자를 지원하면 "PyArg_ParseTupleAndKeywords()"를 사용하
   여 인자를 추출하십시오. 키워드 인자를 지원하지 않고 이것이 "NULL"이
   아니면, 키워드 인자가 지원되지 않는다는 메시지와 함께 "TypeError"를
   발생시키십시오.

장난감 "tp_call" 구현은 다음과 같습니다:

   static PyObject *
   newdatatype_call(newdatatypeobject *obj, PyObject *args, PyObject *kwds)
   {
       PyObject *result;
       const char *arg1;
       const char *arg2;
       const char *arg3;

       if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {
           return NULL;
       }
       result = PyUnicode_FromFormat(
           "Returning -- value: [%d] arg1: [%s] arg2: [%s] arg3: [%s]\n",
           obj->obj_UnderlyingDatatypePtr->size,
           arg1, arg2, arg3);
       return result;
   }

   /* 이터레이터 */
   getiterfunc tp_iter;
   iternextfunc tp_iternext;

이 함수는 이터레이터 프로토콜 지원을 제공합니다. 두 처리기 모두 정확히
하나의 매개 변수, 호출되는 인스턴스를 취하고 새 참조를 반환합니다. 에
러가 발생하면, 예외를 설정하고 "NULL"을 반환해야 합니다. "tp_iter"는
파이썬 "__iter__()" 메서드에 해당하고, "tp_iternext"는 파이썬
"__next__()" 메서드에 해당합니다.

모든 *이터러블* 객체는 *이터레이터* 객체를 반환해야 하는 "tp_iter" 처
리기를 구현해야 합니다. 다음은 파이썬 클래스에도 적용되는 공통 지침입
니다:

* 여러 개의 독립 이터레이터를 지원할 수 있는 컬렉션(가령 리스트와 튜플
  )의 경우, "tp_iter"를 호출할 때마다 새 이터레이터가 만들어지고 반환
  되어야 합니다.

* 한 번만 이터레이트 될 수 있는 (보통 파일 객체처럼 이터레이션의 부작
  용으로 인해) 객체는 스스로에 대한 새로운 참조를 반환하여 "tp_iter"를
  구현할 수 있습니다 -- 따라서 "tp_iternext" 처리기도 구현해야 합니다.

모든 *이터레이터* 객체는 "tp_iter"와 "tp_iternext"를 모두 구현해야 합
니다. 이터레이터의 "tp_iter" 처리기는 이터레이터에 대한 새로운 참조를
반환해야 합니다. "tp_iternext" 처리기는 이터레이션의 다음 객체(있다면)
에 대한 새 참조를 반환해야 합니다. 이터레이션이 끝에 도달하면,
"tp_iternext"는 예외를 설정하지 않고 "NULL"을 반환하거나, "NULL"을 반
환하는 것에 *더해* "StopIteration"을 설정할 수 있습니다; 예외를 피하면
성능이 약간 향상될 수 있습니다. 실제 에러가 발생하면, "tp_iternext"는
항상 예외를 설정하고, "NULL"을 반환해야 합니다.


3.6. 약한 참조 지원
===================

파이썬의 약한 참조 구현의 목표 중 하나는 성능에 중요한 객체(가령 숫자)
에 대한 부하를 발생시키지 않고 모든 형이 약한 참조 메커니즘에 참여할
수 있도록 하는 것입니다.

더 보기: "weakref" 모듈에 대한 설명서.

객체가 약하게 참조될 수 있으려면, 확장형이 "tp_flags" 필드의
"Py_TPFLAGS_MANAGED_WEAKREF" 비트를 설정해야 합니다. 레거시
"tp_weaklistoffset" 필드는 0으로 유지되어야 합니다.

구체적으로, 정적으로 선언된 형 객체는 이런 식입니다:

   static PyTypeObject TrivialType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       /* ... 간결성을 위해 생략된 다른 멤버들 ... */
       .tp_flags = Py_TPFLAGS_MANAGED_WEAKREF | ...,
   };

유일한 추가 사항은 "tp_dealloc"이 ("PyObject_ClearWeakRefs()"를 호출하
여) 모든 약한 참조를 지울 필요가 있다는 것입니다:

   static void
   Trivial_dealloc(TrivialObject *self)
   {
       /* 파괴자를 호출하기 전에 먼저 약한 참조를 지웁니다 */
       PyObject_ClearWeakRefs((PyObject *) self);
       /* ... 간결성을 위해 생략된 나머지 파괴 코드 ... */
       Py_TYPE(self)->tp_free((PyObject *) self);
   }


3.7. 추가 제안
==============

새 데이터형에 특정 메서드를 구현하는 방법을 배우려면, *CPython* 소스
코드를 구하십시오. "Objects" 디렉터리로 이동한 다음, C 소스 파일에서
"tp_"에 원하는 기능을 더한 것(예를 들어, "tp_richcompare")을 검색하십
시오. 구현하려는 함수의 예를 찾을 수 있을 겁니다.

객체가 구현 중인 형의 구상 인스턴스인지 확인해야 하면,
"PyObject_TypeCheck()" 함수를 사용하십시오. 사용 예는 다음과 같습니다:

   if (!PyObject_TypeCheck(some_object, &MyType)) {
       PyErr_SetString(PyExc_TypeError, "arg #1 not a mything");
       return NULL;
   }

더 보기:

  CPython 소스 릴리스를 다운로드하십시오.
     https://www.python.org/downloads/source/

  GitHub의 CPython 프로젝트, CPython 소스 코드가 개발되는 곳.
     https://github.com/python/cpython
