소개¶
flowdas
이 페이지는 SeongHyeon Kim 님의 번역입니다.
파이썬의 애플리케이션 프로그래머용 인터페이스는 다양한 수준에서 C/C++ 프로그래머에게 파이썬 인터프리터에 대한 접근 방법을 제공합니다. 이 API는 C++에서도 동일하게 사용 가능하지만 간결함을 위해 보통 파이썬/C API 로 불립니다. 파이썬/C API를 사용하는 데에는 근본적으로 다른 두 가지 이유가 있습니다. 첫번째 이유는 특정한 목적을 위해 확장 모듈을 작성하기 위해서입니다; 이 확장 모듈들은 파이썬 인터프리터를 확장하는 C 모듈들입니다. 이것이 아마도 가장 흔한 용도일 것입니다. 두번째 이유는 파이썬을 더 큰 애플리케이션의 컴포넌트로 사용하기 위함입니다. 이 기술은 일반적으로 파이썬을 애플리케이션에 임베딩(embedding) 하는 것을 말합니다.
확장 모듈을 작성하는 것은 비교적 잘 다듬어진 과정으로, "쿡북" 접근법이 잘 통하며 프로세스를 다소 자동화하는 툴들도 존재합니다. 사람들은 파이썬이 존재한 초기부터 다른 애플리케이션에 파이썬을 임베드 해왔으나 파이썬을 임베딩 하는 과정은 확장 모듈을 작성하는 것보다 복잡합니다.
많은 API 함수들은 파이썬을 임베딩하거나 확장하는 것에 무관하게 유용합니다. 더욱이 파이썬을 임베드하는 대부분의 애플리케이션은 커스텀 확장을 제공할 필요성이 있기 때문에 파이썬을 임베드하려고 시도하기 전에 확장을 작성하는 것에 친숙해지는 것이 좋습니다.
코딩 표준¶
CPython 에 포함하기 위해 C 코드를 작성하는 경우에는 PEP 7 에 정의된 지침과 표준을 따라야 합니다. 이 지침은 기여하고 있는 파이썬 버전과 상관없이 적용됩니다. 최종적으로 파이썬에 기여하는 것을 기대하지 않는 이상 이 규칙을 따르는 것은 제삼자 확장 모듈에는 필수가 아닙니다.
인클루드 파일¶
파이썬/C API를 사용하기 위한 모든 함수, 타입 그리고 매크로 정의는 다음 행에 의해 인클루드됩니다.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
이는 다음과 같은 표준 헤더를 인클루드하는것을 의미합니다: <stdio.h>
, <string.h>
, <errno.h>
, <limits.h>
, <assert.h>
그리고 <stdlib.h>
(사용 가능한 경우).
참고
파이썬은 일부 시스템의 표준 헤더에 영향을 미치는 전처리기 정의를 정의할 수 있으므로 표준 헤더를 인클루드하기 전에 Python.h
를 인클루드해야 합니다.
Python.h
를 인클루드하기 전에 항상 PY_SSIZE_T_CLEAN
를 정의하는 것을 권장합니다. 이 매크로에 대한 자세한 사항은 인자 구문 분석과 값 구축 을 참조하십시오.
Python.h로 정의된 사용자에게 공개되는 모든 이름들은 (포함된 표준 헤더로 정의된 것은 제외) Py
또는 _Py
로 시작하는 이름을 가지고 있습니다. _Py
로 시작하는 이름들은 파이썬 구현에 의해 내부적으로 사용되며 확장 개발자들에 의해 사용돠어서는 안됩니다. 구조체 멤버들은 이름에 접두사가 붙지 않습니다.
참고
사용자 코드는 Py
또는 _Py
로 시작하는 이름들을 정의해서는 안됩니다. 이것은 읽는 사람을 혼란스럽게 하며이러한 접두사가 붙는 추가적인 이름을 정의할수도 있는 향후의 파이썬 버전에 대한 사용자 코드의 이식성을 위태롭게 합니다.
헤더 파일들은 일반적으로 파이썬과 함께 설치됩니다. 유닉스에서는 헤더 파일들은 prefix/include/pythonversion/
와 exec_prefix/include/pythonversion/
안에 들어있습니다. prefix
와 exec_prefix
는 파이썬의 configure 스크립트에 해당하는 파라미터로 정의되며 버전 은 '%d.%d' % sys.version_info[:2]
입니다. 윈도우에서는 헤더 파일들은 prefix/include
안에 설치되어 있습니다. prefix
는 인스톨러에 의해 지정된 설치 디렉터리 입니다.
헤더를 인클루드 하기 위해선 두 디렉터리 모두 (두 디렉터리가 다를 경우) 컴파일러의 검색 패스에 추가하십시오. 부모 디렉터리를 검색 패스에 추가해서 #include <pythonX.Y/Python.h>
처럼 사용해서는 안됩니다. prefix
아래의 플랫폼 독립적인 헤더들이 exec_prefix
에서 플랫폼 종속적인 헤더를 인클루드 하기 때문에 멀티플랫폼 빌드가 고장날 것입니다.
C++ 사용자들은 파이썬/C API 가 C 만을 사용하여 정의되었어도 헤더 파일들이 extern "C"
로 진입점을 제대로 선언한다는 점에 유의해야 합니다. C++ 에서 파이썬/C API 를 사용하기 위해 특별한 조치를 취할 필요는 없습니다.
유용한 매크로들¶
파이썬 헤더 파일에는 몇 가지 유용한 매크로가 정의되어 있습니다. 대부분은 필요한 곳에 가깝게 정의되어 있습니다. (예를 들어 Py_RETURN_NONE
) 나머지 더 일반적인 유틸리티들은 여기에 정의되어 있습니다. 아래 목록이 전체 목록은 아닙니다.
-
Py_UNREACHABLE
()¶ 의도적으로 도달할 수 없는 코드 경로가 있을 경우에 이 매크로를 사용하십시오. 예를 들어,
switch
문에서 가능한 모든 값이case
절에서 다뤄지는 경우에default:
절에서 사용할 수 있습니다.assert(0)
또는abort()
대신 사용하십시오.릴리즈 모드에서 이 매크로는 컴파일러가 코드를 최적화하는데 도움이 되며 도달할 수 없는 코드에 대한 경고를 방지합니다. 예를 들어, 이 매크로는 릴리즈 모드에서 GCC의
__builtin_unreachable()
로 구현됩니다.Py_UNREACHABLE()
의 용도는 반환하지 않지만_Py_NO_RETURN
을 선언하지 않은 함수를 호출하는 것입니다코드 경로가 매우 가능성이 낮지만 예외적인 경우에 도달할 수 있는 경우, 이 매크로를 사용해서는 안됩니다. 예를 들어, 메모리가 부족하거나 시스템 콜이 예상 범위를 벗어나는 값을 반환했을 경우에는 호출자에게 에러를 보고하는 것이 좋습니다. 호출자에게 에러를 보고할 수 없는 경우
Py_FatalError()
를 사용할 수 있습니다.버전 3.7에 추가.
-
Py_ABS
(x)¶ x
의 절댓값을 반환합니다.버전 3.3에 추가.
-
Py_MIN
(x, y)¶ x
와y
사이의 최솟값을 반환합니다.버전 3.3에 추가.
-
Py_MAX
(x, y)¶ x
와y
사이의 최댓값을 반환합니다.버전 3.3에 추가.
-
Py_STRINGIFY
(x)¶ x
를 C 문자열로 변환합니다. 예를 들어Py_STRINGIFY(123)
은"123"
을 반환합니다.버전 3.4에 추가.
-
Py_MEMBER_SIZE
(type, member)¶ (
type
) 구조체의member
의 크기를 바이트로 반환합니다.버전 3.6에 추가.
-
Py_CHARMASK
(c)¶ 인자는 문자 또는 [-128, 127] 나 [0, 255] 사이의 정수여야 합니다. 이 매크로는
unsigned char
로 캐스팅된c
를 반환합니다
-
Py_GETENV
(s)¶ getenv(s)
와 유사하지만-E
가 커맨드라인으로 전달된 경우 (즉,Py_IgnoreEnvironmentFlag
가 설정된 경우)NULL
을 반환합니다
-
Py_UNUSED
(arg)¶ 함수의 미사용 인자에 사용하여 컴파일러 경고를 무시합니다. 예시:
int func(int a, int Py_UNUSED(b)) { return a; }
.버전 3.4에 추가.
-
Py_DEPRECATED
(version)¶ 폐지(deprecated) 선언에 사용하십시오. 이 매크로는 심볼 이름 앞에 위치해야 합니다
예제:
Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);
버전 3.8에서 변경: MSVC 지원을 추가했습니다.
-
PyDoc_STRVAR
(name, str)¶ 독스트링에서 사용 가능한
name
이란 이름의 변수를 생성합니다. 파이썬이 독스트링 없이 빌드되었다면 변수의 값은 비어있을 것입니다.PEP 7 에 명시된 것처럼 파이썬을 독스트링 없이 빌드하기 위해
PyDoc_STRVAR
를 독스트링에 사용하십시오예제:
PyDoc_STRVAR(pop_doc, "Remove and return the rightmost element."); static PyMethodDef deque_methods[] = { // ... {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc}, // ... }
-
PyDoc_STR
(str)¶ 주어진 문자열에 대한 독스트링을 생성합니다. 독스트링이 비활성화 되어있을 경우엔 빈 문자열을 생성합니다.
PEP 7 에 명시된 것처럼 독스트링 없이 파이썬을 빌드할 수 있도록 독스트링을 명시할 때
PyDoc_STR
을 사용하십시오.예제:
static PyMethodDef pysqlite_row_methods[] = { {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, PyDoc_STR("Returns the keys of the row.")}, {NULL, NULL} };
객체, 형 그리고 참조 횟수¶
대부분의 파이썬/C API 함수는 하나 이상의 인자와 PyObject*
형의 반환 값을 가집니다. 해당 형은 임의의 파이썬 객체를 나타내는 오파크(Opaque) 데이터 형에 대한 포인터입니다. 할당, 스코프 규칙, 인자 전달 등 대부분의 상황에서 파이썬 언어가 모든 파이썬 객체 형을 동일한 방식으로 취급하기 때문에 단일한 C 형으로 표현하는 것이 적절합니다. 대부분의 객체는 힙에 존재합니다. PyObject
형의 자동 변수나 정적 변수를 선언해서는 안되며 PyObject
형의 포인터 변수만을 선언할 수 있습니다. 유일한 예외는 형 객체입니다. 형 객체들은 절대 할당이 해제돼서는 안되기 때문에 형 객체들은 보통 정적 PyTypeObject
객체들입니다.
모든 파이썬 객체는 (파이썬 정수조차도) 형(type) 과 참조 횟수(reference count) 를 가지고 있습니다. 객체의 형은 객체의 종류를 결정합니다. (예를 들어 정수, 리스트, 또는 사용자 정의 함수 등. 표준형 계층 에 추가적인 형들에 대해 설명되어 있습니다.) 잘 알려진 형에는 객체가 해당 형인지를 확인하는 매크로가 있습니다. 예를 들어 PyList_Check(a)
는 a 가 가리키는 객체가 파이썬 리스트일 경우에만 참입니다.
참조 횟수¶
오늘날의 컴퓨터는 유한한(그리고 자주 제한되는) 양의 메모리를 가지고 있기 때문에. 참조 횟수는 매우 중요합니다. 참조 횟수는 객체를 참조하는 위치의 갯수를 셉니다. 객체를 참조하는 위치는 다른 객체일 수도 있고, C 전역 변수이거나 C 함수 안의 지역 변수일 수도 있습니다. 객체의 참조 횟수가 0이 되면 객체는 할당이 해제됩니다. 객체가 다른 객체에 대한 참조를 가지고 있을 경우 해당 객체의 참조 횟수가 감소합니다. 이러한 참조 횟수의 감소로 인해 다른 객체의 참조 횟수가 0으로 감소하면 다른 객체들이 차례로 할당이 해제될 수 있습니다. (서로를 참조하는 객체에 대해 분명한 문제점이 있으나 현재 해결책은 "그러지 마세요" 입니다.)
참조 횟수는 항상 명시적으로 조작됩니다. 일반적인 방법은 매크로 Py_INCREF()
를 사용하여 객체의 참조 횟수를 하나 증가시키고, Py_DECREF()
를 사용하여 참조 횟수를 하나 감소시키는 것입니다. Py_DECREF()
매크로는 참조 횟수가 0이 되는지를 확인하고 객체의 할당해제자(deallocator) 를 호출해야 하므로 참조 횟수를 증가시키는 매크로보다 상당히 복잡합니다. 할당 해제자는 객체의 형 구조체에 포함된 함수 포인터입니다. 형별 할당 해제자는 리스트와 같은 복합 객체 형에 대해서 객체에 포함된 객체에 포함 된 다른 객체에 대한 참조 횟수를 감소시키고 필요한 추가 마무리 작업을 수행합니다. 참조 횟수가 오버플로우 될 가능성은 없습니다. 참조 횟수를 저장하기 위해서 가상 메모리 안에서 구별되는 메모리 위치 만큼 (sizeof(Py_ssize_t) >= sizeof(void*)
라고 가정할 경우) 많은 비트가 사용됩니다. 따라서 참조 횟수 증가는 간단한 연산입니다
객체에 대한 포인터를 포함하는 모든 지역 변수에 대해 객체의 참조 횟수를 증가시킬 필요는 없습니다. 이론적으로는 객체의 참조 횟수는 변수가 객체를 가리킬 때 증가하고 변수가 스코프를 벗어날 때 감소합니다. 하지만 이 두 동작은 서로를 상쇄시키기 때문에 결과적으로 참조 횟수는 변하지 않습니다. 참조 횟수를 사용해야만 하는 이유는 우리의 변수가 객체를 참조하는 한 객체가 할당 해제되지 않도록 하기 위함입니다. 만약 객체에 대한 적어도 하나 이상의 다른 참조가 우리의 변수만큼 오래 살아있을 것을 안다면 참조 횟수를 일시적으로 증가시킬 필요가 없습니다. 이런 현상이 발생하는 중요한 상황은 파이썬이 호출하는 확장 모듈의 C 함수에 인자로 넘겨지는 객체에서 발생합니다. 호출 메커니즘은 호출하는동안 모든 인자에 대한 참조를 보유하도록 보장합니다.
그러나 흔히 하기 쉬운 실수는 리스트에서 객체를 가져와 참조 횟수를 늘리지 않고 유지하는 것입니다. 다른 연산이 리스트에서 객체를 제거해 객체의 참조 횟수를 감소시키면 객체가 할당 해제될수도 있습니다. 진짜 위험은 무해해 보이는 연산이 이를 실행할 수도 있는 임의의 파이썬 코드를 실행할수도 있다는 것입니다. Py_DECREF()
에서 사용자로 컨트롤을 돌아가게 하는 코드 경로가 있기 때문에 사실 거의 모든 연산이 잠재적으로 위험합니다.
안전한 접근방식은 제네릭 연산들 (PyObject_
, PyNumber_
, PySequence_
또는 PyMapping_
로 시작하는 이름을 가진 함수들) 을 사용하는 것입니다. 이러한 연산들은 반환하는 모든 객체에 대한 참조 횟수를 증가시킵니다. 이는 함수 호출자에게 연산이 반환된 객체와의 작업이 끝났을 경우 Py_DECREF()
를 호출할 책임을 지웁니다. 이건 곧 습관화 될 것입니다.
참조 횟수 상세¶
파이썬/C API 에서 함수의 참조 횟수 동작은 참조의 소유권 으로 가장 잘 설명됩니다. 소유권은 객체가 아닌 참조에 관련되어 있습니다. (객체는 소유되지 않습니다: 객체는 항상 공유됩니다.) "참조를 소유하는 것" 은 더 이상 참조가 필요하지 않을 때 Py_DECREF 를 호출할 책임이 있다는 것을 의미합니다. 소유권은 양도될 수도 있습니다. 즉, 참조의 소유권을 받은 코드가 소유권이 더 이상 필요하지 않을 때 Py_DECREF()
또는 Py_XDECREF()
를 호출하거나 책임을 (대게 자신의 호출자에게) 넘김으로써 결국 참조 횟수를 감소시킬 책임이 생긴다는 것을 의미합니다. 함수가 참조의 소유권을 호출자에게 넘길때 호출자는 새로운 참조를 받는다고 합니다. 소유권이 옮겨지지 않을 때 호출자는 참조를 빌린다 라고 합니다. 빌린 참조를 위해서는 아무것도 할 필요가 없습니다.
반대로 호출 함수에게 객체에 대한 참조를 넘길 때는 두가지 가능성이 있습니다: 함수는 객체에 대한 참조를 훔칠 수도, 그러지 않을 수도 있습니다. 참조를 훔치는 것은 함수에 참조를 전달할 때 해당 함수가 전달된 참조를 소유한다고 가정하고 더 이상 책임을 지지 않는다는 것을 의미합니다.
참조를 훔치는 함수는 거의 없습니다. 주목할만한 두가지 예외는 PyList_SetItem()
과 PyTuple_SetItem()
입니다. 이 두가지 함수는 요소에 대한 참조를 훔칩니다(단, 요소를 넣을 튜플이나 리스트에 대한 참조는 훔치지 않습니다.). 이 함수들은 새로 만들어진 객체들로 튜플이나 리스트를 채우는 일반적인 관행 때문에 참조를 훔치도록 설계되었습니다. 예를 들어, 튜플을 만드는 코드 (1, 2, "three")
는 다음과 같을 수 있습니다. (잠시 에러 처리는 잊어버리십시오. 더 좋은 방법으로 코딩하는 방법은 아래에 나와 있습니다.)
PyObject *t;
t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyLong_FromLong(1L));
PyTuple_SetItem(t, 1, PyLong_FromLong(2L));
PyTuple_SetItem(t, 2, PyUnicode_FromString("three"));
여기서 PyTuple_SetItem()
는 PyLong_FromLong()
가 반환한 참조를 곧바로 훔칩니다. 객체에 대한 참조가 훔쳐져도 계속 객체를 사용하려면 참조를 훔치는 함수를 호출하기 전에 Py_INCREF()
를 다른 참조를 가져오는데 사용하십시오.
덧붙이자면, PyTuple_SetItem()
은 튜플에 요소를 넣는 유일한 방법입니다. 튜플은 불변 자료형이기 때문에 PySequence_SetItem()
과 PyObject_SetItem()
는 튜플에 요소를 넣는 것을 거부합니다. PyTuple_SetItem()
은 직접 만들고 있는 튜플에만 사용되어야 합니다.
리스트를 채우는 동일한 의미의 코드는 PyList_New()
와 PyList_SetItem()
을 사용해 만들 수 있습니다.
하지만 실제로는 이렇게 튜플 또는 리스트를 만들고 채우는 경우는 드뭅니다. 일반적인 객체들을 형식 문자열(format string)로 지시되는 C 값으로부터 만들어낼 수 있는 제네릭 함수 Py_BuildValue()
가 있습니다. 예룰 들어, 위의 두 블록의 코드를 다음 코드로 대체할 수 있습니다. (에러 검사도 처리합니다.)
PyObject *tuple, *list;
tuple = Py_BuildValue("(iis)", 1, 2, "three");
list = Py_BuildValue("[iis]", 1, 2, "three");
PyObject_SetItem()
과 그 친구들은 함수에 전달되는 인자처럼 참조만 빌리고 있는 요소와 함께 사용하는 것이 일반적입니다. 이 경우 참조 횟수를 증가시키지 않고 참조를 제공할 수 있으므로 참조 카운트에 대한 함수들의 동작이 훨씬 멀쩡합니다:
int
set_all(PyObject *target, PyObject *item)
{
Py_ssize_t i, n;
n = PyObject_Length(target);
if (n < 0)
return -1;
for (i = 0; i < n; i++) {
PyObject *index = PyLong_FromSsize_t(i);
if (!index)
return -1;
if (PyObject_SetItem(target, index, item) < 0) {
Py_DECREF(index);
return -1;
}
Py_DECREF(index);
}
return 0;
}
함수 반환 값에 대해서는 상황이 약간 다릅니다. 대부분의 함수에 참조를 전달해도 해당 참조에 대한 소유권 책임이 바뀌진 않지만 객체에 대한 참조를 제공하는 많은 함수는 참조의 소유권을 제공합니다. 이유는 간단합니다. 대부분의 경우에서 반환된 객체는 즉석에서 생성되고 반환된 참조는 객체에 대한 유일한 참조입니다. 따라서 PyObject_GetItem()
과 PySequence_GetItem()
처럼 객체에 대한 참조를 반환하는 제네릭 함수들은 언제나 새로운 참조를 반환합니다 (호출자가 객체의 소유자가 됩니다).
함수의 의해 반환된 함수를 소유하고 있는지는 어떤 함수를 호출하느냐에 따라 달라진다는 것을 아는 것이 중요합니다. --- 깃털 (함수에 인자로 전달된 객체의 형) 은 해당되지 않습니다! 따라서 PyList_GetItem()
를 사용하여 리스트에서 항목을 가져오면 참조를 소유하지 않습니다. --- 하지만 동일한 인자를 받는 PySequence_GetItem()
를 사용하여 리스트에서 항목을 가져온다면 반환된 객체에 대한 참조를 소유하게 됩니다.
다음은 정수 리스트에 있는 항목의 합계를 구하는 함수를 작성하는 방법의 예시입니다. 한 번은 PyList_GetItem()
를 사용하고, 한 번은 PySequence_GetItem()
을 사용합니다.
long
sum_list(PyObject *list)
{
Py_ssize_t i, n;
long total = 0, value;
PyObject *item;
n = PyList_Size(list);
if (n < 0)
return -1; /* Not a list */
for (i = 0; i < n; i++) {
item = PyList_GetItem(list, i); /* Can't fail */
if (!PyLong_Check(item)) continue; /* Skip non-integers */
value = PyLong_AsLong(item);
if (value == -1 && PyErr_Occurred())
/* Integer too big to fit in a C long, bail out */
return -1;
total += value;
}
return total;
}
long
sum_sequence(PyObject *sequence)
{
Py_ssize_t i, n;
long total = 0, value;
PyObject *item;
n = PySequence_Length(sequence);
if (n < 0)
return -1; /* Has no length */
for (i = 0; i < n; i++) {
item = PySequence_GetItem(sequence, i);
if (item == NULL)
return -1; /* Not a sequence, or other failure */
if (PyLong_Check(item)) {
value = PyLong_AsLong(item);
Py_DECREF(item);
if (value == -1 && PyErr_Occurred())
/* Integer too big to fit in a C long, bail out */
return -1;
total += value;
}
else {
Py_DECREF(item); /* Discard reference ownership */
}
}
return total;
}
형¶
파이썬/C API에서 중요한 역할을 하는 다른 데이터 형은 거의 없습니다. 대부분은 int
, long
, double
그리고 char*
같은 평범한 C 형입니다. 모듈에서 내보내는 함수나 새 객체 형의 데이터 속성들을 나열하는데 사용되는 정적 테이블을 표현하는데 사용되는 구조체 형이 몇가지 있으며, 복소수를 표현하기 위해 사용되는 구조체 형도 있습니다. 이러한 형들은 해당 형들을 사용하는 함수와 함께 다뤄질 것입니다.
예외¶
파이썬 프로그래머는 특정한 에러 처리가 필요할 경우에만 에러를 처리하면 됩니다. 처리되지 않은 예외는 사용자에게 전달되는 최상위 인터프리터까지 스택 트레이스백과 함께 자동으로 호출자, 호출자의 호출자 등으로 전파됩니다.
그러나 C 프로그래머들에게 에러 검사는 항상 명시적이어야만 합니다. 파이썬/C API의 모든 함수는 해당 함수의 문서에서 명시하지 않는 한 예외를 발생시킬 수 있습니다. 일반적으로 함수에 에러가 발생하면 함수는 예외를 설정하고 소유하고 있는 모든 객체에 대한 참조를 취소하고 에러 표시기를 반환합니다. 달리 문서화되지 않은 경우 표시기는 함수의 반환 형에 따라 NULL
또는 -1
입니다. 일부 함수는 에러를 의미하는 거짓과 함께 참/거짓의 불리언 결과를 반환합니다. 아주 일부의 함수는 명시적인 에러 표시기가 없거나 모호한 반환값을 가지며 PyErr_Occurred()
를 사용하여 명시적인 점검을 요구합니다. 이런 예외는 항상 명시적으로 문서화됩니다.
예외 상태는 스레드 별 공간에서 관리됩니다. (스레드를 사용하지 않는 프로그램에서는 전역 공간을 사용한다는 말과 같습니다.) 스레드는 예외가 발생했거나, 발생하지 않았거나의 두가지 상태 중 하나일 수 있습니다. 함수 PyErr_Occurred()
는 이 상태를 확인하기 위해 사용할 수 있습니다. 해당 함수는 예외가 발생했을 경우 예외 형 객체에 대한 빌린 참조를 반환합니다. 예외가 발생하지 않았을 경우엔 NULL
을 반환합니다. 예외 상태를 설정하기 위한 여러가지 함수들이 있습니다: PyErr_SetString()
는 예외 상태를 설정하기 위해 가장 보편적인 (가장 일반적인 것은 아니지만) 함수입니다. PyErr_Clear()
는 예외 상태를 지웁니다.
전체 예외 상태는 예외 형, 해당 예외 값, 트레이스백이라는 세가지 객체로 구성됩니다. (셋 모두 NULL
일 수 있습니다.) 이 세가지 객체는 파이썬의 sys.exc_info()
의 결과와 같은 의미를 가지고 있지만 동일하지는 않습니다. 파이썬 객체는 try
... except
문으로 처리되는 마지막 예외를 표현하는 반면 C 수준 예외는 sys.exc_info()
와 그 친구들로 예외를 전송하는 파이썬 바이트코드 인터프리터의 메인 루프에 도달할 때까지 C 함수들 간에 전달되는 동안에만 존재합니다.
파이썬 1.5부터 선호되어 온 파이썬 코드에서의 스레드 안전한 예외 상태 접근 방법은 파이썬 코드를 위해 스레드 별 예외 상태를 반환하는 sys.exc_info()
함수를 호출하는 것입니다. 또한 예외 상태에 접근하는 양쪽 방법의 의미도 바뀌어 에러를 포착하는 함수가 호출자의 예외 상태를 보존하기 위해 스레드의 예외를 저장하고 복원합니다. 이는 평범해 보이는 함수가 처리중인 예외를 덮어씌우는 것으로 인한 예외 처리 코드의 흔한 버그를 방지합니다. 또한 트레이스백의 스택 프레임에 의하여 참조되는 객체들에 대해 종종 원하지 않은 수명 증가가 일어나는 것을 방지합니다
일반적으로 어떤 작업을 수행하기 위해 다른 함수를 호출하는 함수는 호출된 함수가 예외를 일으켰는지 확인해야만 하며 만약 예외가 일어났다면 호출자에게 예외 상태를 전달해야 합니다. 소유하고 있는 모든 객체에 대한 참조를 버리고 에러 표시기를 반환해야 하지만 다른 예외를 설정해서는 안됩니다. --- 방금 일어난 예외를 덮어씌우고 정확한 에러 원인에 대한 중요한 정보를 잃어버리게 됩니다.
예외를 감지하고 전달하는 간단한 예가 아래 sum_sequence()
예시에 나와 있습니다. 우연히 이 예시에선 에러를 감지했을 때 소유하고 있는 참조를 정리할 필요가 없습니다. 그 다음 예시 함수에서는 몇가지 에러 정리작업을 보여줍니다. 먼저 파이썬을 좋아하는 이유를 상기시키기 위해 같은 의미의 파이썬 코드를 제공합니다:
def incr_item(dict, key):
try:
item = dict[key]
except KeyError:
item = 0
dict[key] = item + 1
다음은 같은 의미의 웅장한 C 코드입니다:
int
incr_item(PyObject *dict, PyObject *key)
{
/* Objects all initialized to NULL for Py_XDECREF */
PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
int rv = -1; /* Return value initialized to -1 (failure) */
item = PyObject_GetItem(dict, key);
if (item == NULL) {
/* Handle KeyError only: */
if (!PyErr_ExceptionMatches(PyExc_KeyError))
goto error;
/* Clear the error and use zero: */
PyErr_Clear();
item = PyLong_FromLong(0L);
if (item == NULL)
goto error;
}
const_one = PyLong_FromLong(1L);
if (const_one == NULL)
goto error;
incremented_item = PyNumber_Add(item, const_one);
if (incremented_item == NULL)
goto error;
if (PyObject_SetItem(dict, key, incremented_item) < 0)
goto error;
rv = 0; /* Success */
/* Continue with cleanup code */
error:
/* Cleanup code, shared by success and failure path */
/* Use Py_XDECREF() to ignore NULL references */
Py_XDECREF(item);
Py_XDECREF(const_one);
Py_XDECREF(incremented_item);
return rv; /* -1 for error, 0 for success */
}
이 예시는 C goto
문의 허용된 사용방법을 보여줍니다! 이 예시는 특정한 예외를 처리하기 위한 PyErr_ExceptionMatches()
와 PyErr_Clear()
의 사용 방법과 Py_XDECREF()
를 사용하여 소유하고 있는 NULL
일 수도 있는 참조를 삭제하는 방법을 표현합니다. (이름에 있는 'X'
를 주목하십시오. Py_DECREF()
는 NULL
참조와 마주치면 충돌을 일으킵니다.) 이 예시를 수행하려면 소유하고 있는 참조를 보유하는데 사용하는 변수를 NULL
로 초기화하는 것이 중요합니다. 마찬가지로 반환 값은 -1
(실패) 로 설정되고 마지만 호출이 성공한 뒤에야 성공으로 설정됩니다.
파이썬 임베딩하기¶
확장 작성자들과는 달리 파이썬 인터프리터를 임베딩 하는 사람들만이 걱정해야 하는 한가지 중요한 문제는 파이썬 인터프리터의 초기화, 그리고 아마도 마무리일 것입니다. 인터프리터의 대부분의 기능은 인터프리터가 초기화 된 이후에 사용할 수 있습니다.
기본적인 초기화 함수는 Py_Initialize()
입니다. 이 함수는 로드된 모듈 테이블을 초기화 하고 기본 모듈인 builtins
, __main__
, 그리고 sys
를 생성합니다. 또한 모듈 검색 경로 (sys.path
) 를 초기화합니다
Py_Initialize()
는 "스크립트 인자 리스트" (sys.argv
) 를 설정하지 않습니다. 만약 후에 실행될 파이썬 코드가 이 변수를 필요로 한다면 Py_Initialize()
를 호출한 후 PySys_SetArgvEx(argc, argv, updatepath)
호출을 통해 명시적으로 설정해야 합니다.
대부분의 시스템에서 (특별히 유닉스와 윈도우는 세부적인 부분이 조금 다르긴 하지만) Py_Initialize()
는 파이썬 인터프리터를 기준으로 고정된 위치에 파이썬 라이브러리가 있다고 가정하여 표준 파이썬 인터프리터 실행 파일에 대한 최선의 추측을 바탕으로 바탕으로 모듈 검색 경로를 계산합니다. 특히 셸 명령어 검색 경로 (환경 변수 PATH
) 에서 python
이라는 이름의 실행 파일이 발견되는 부모 디렉터리를 기준으로 lib/pythonX.Y
같은 이름을 가진 디렉터리를 찾습니다.
예를 들어 파이썬 실행 파일이 /usr/local/bin/python
에서 발견된다면 라이브러리는 /usr/local/lib/pythonX.Y
에 있는 것으로 가정합니다. (실제로 이 특정 경로는 PATH
를 따라 python
이라는 이름의 실행 파일이 발견되지 않을 때 사용되는 "fallback" 경로이기도 합니다.) 유저는 환경 변수 PYTHONHOME
를 설정하여 이 동작을 재정의하거나 PYTHONPATH
를 설정하여 표준 경로 앞에 추가적인 디렉터리를 추가할 수 있습니다.
파이썬을 임베딩하는 애플리케이션은 Py_Initialize()
를 호출하기 전에 Py_SetProgramName(file)
을 호출하여 검색을 제어할 수 있습니다. PYTHONHOME
는 여전히 이 값을 재정의하며 PYTHONPATH
는 여전히 표준 경로 앞에 추가된다는 점에 유의하십시오. 완전한 제어가 필요한 애플리케이션은 Py_GetPath()
, Py_GetPrefix()
, Py_GetExecPrefix()
, 그리고 Py_GetProgramFullPath()
의 자체적인 구현을 제공할 필요가 있습니다. (모두 Modules/getpath.c
에 정의되어 있습니다.)
가끔은 파이썬을 "uninitialize" 하는 것이 바람직합니다. 예를 들어 애플리케이션이 다시 시작하거나 (Py_Initialize()
다시 호출하기) 애플리케이션에서 파이썬의 사용이 끝나 파이썬이 할당한 메모리를 해제하려고 할 수 있습니다. Py_FinalizeEx()
를 호출하여 이를 달성할 수 있습니다. 함수 Py_IsInitialized()
는 파이썬이 현재 초기화된 상태에 있을 경우 참을 반환합니다. 이 함수들에 대한 자세한 내용은 다른 장에서 제공됩니다. Py_FinalizeEx()
가 파이썬 인터프리터가 할당한 모든 메모리를 해제하지는 않는다는 점에 유의해야 합니다. 예를 들어, 현재 확장 모듈에서 할당한 메모리는 해제할 수 없습니다.
디버깅 빌드¶
파이썬은 인터프리터와 확장 모듈들에 대한 추가적인 검사를 가능하게 하는 여러 매크로를 사용하여 빌드될 수 있습니다. 이러한 검사는 런타임에 많은 오버헤드를 추가하는 경향이 있으므로 기본적으로 실행되지 않습니다.
다양한 유형의 디버깅 빌드의 전체 목록은 파이썬 소스 배포판 안의 Misc/SpecialBuilds.txt
파일에 있습니다. 참조 횟수 추적, 메모리 할당자 디버깅, 메인 인터프리터 루프의 저수준 프로파일링을 지원하는 빌드들을 사용할 수 있습니다. 이 섹션에서는 가장 자주 사용되는 빌드만 설명합니다.
Py_DEBUG
매크로가 정의된 인터프리터를 컴파일하면 일반적으로 파이썬의 "디버그 빌드" 가 일반적으로 의미하는 빌드가 생성됩니다. Py_DEBUG
는 ./configure
명령에 --with-pydebug
를 추가하여 유닉스 빌드에서 활성화됩니다. 또한 파이썬 전용이 아닌 _DEBUG
매크로의 존재를 암시합니다. 유닉스 빌드에서 Py_DEBUG
가 활성화 되어있으면 컴파일러 최적화가 비활성화됩니다.
아래에 설명된 참조 횟수 디버깅 외에도 다음과 같은 추가적인 검사가 수행됩니다:
객체 할당자에 추가적인 검사가 추가됩니다.
파서 및 컴파일러에 추가적인 검사가 추가됩니다.
큰 타입에서 작은 타입으로 다운 캐스팅이 일어날 때 정보 손실을 확인합니다.
많은 어설션이 딕셔너리와 집합 구현에 추가됩니다. 또한 집합 객체는
test_c_api()
메소드가 추가됩니다.입력 인자의 온전성 검사가 프레임 생성에 추가됩니다.
정수에 대한 저장소는 초기화되지 않은 숫자에 대한 참조를 포착하기 위해 알려진 잘못된 패턴으로 초기화됩니다.
저수준 추적과 추가적인 예외 검사가 런타임 가상머신에 추가됩니다.
메모리 아레나 구현에 추가 검사가 추가됩니다.
스레드 모듈에 추가적인 디버깅이 추가됩니다.
여기에 언급되지 않은 추가적인 검사들이 있을 수 있습니다.
Py_TRACE_REFS
를 정의하면 참조 추적이 활성화됩니다. 정의된 경우 모든 PyObject
에 두 개의 추가 필드를 추가함으로써 활성 객체에 대한 이중 원형 연결 리스트가 유지됩니다. 또한 모든 할당이 추적됩니다. 인터프리터 종료시에는 현재 사용되고 있는 모든 참조가 출력됩니다. (대화식 모드에서는 인터프리터가 명령문을 실행할 때마다 이후에 출력됩니다.) Py_DEBUG
에 의해 암시됩니다.
자세한 내용은 파이썬 소스 배포판 안의 Misc/SpecialBuilds.txt
를 참조하십시오.