본문 바로가기
my_lesson/_Python

Python - C/C++ API Reference Manual Introduction

by boolean 2019. 4. 19.
728x90

Embedding & Extended Python /  C&CPP

Introduction

Python 인터페이스를 통해 C 및 C ++ 프로그래머는 Python 인터프리터에 액세스 할 수 있다.
API는 C ++에서도 똑같이 사용할 수 있다.


특정 목적을 위해 확장 모듈을 작성.
이들은 파이썬 인터프리터를 확장하는 C 모듈입니다. 아마도 가장 일반적인 용도 일 것입니다.
큰 응용 프로그램에서 파이썬을 구성 요소로 사용
이 기술은 일반적으로 응용 프로그램에 파이썬 임베딩이라고합니다.

 

일반적으로 확장하는 것이 임베딩하는 것보다 슆다.

 

Python에서 제공하는 API함수는 확장하거나 임베딩하는 것과 관계 없이 유용하지만 임베딩하는 것보다 확장기능에 먼저 익숙해지기를 추천한다.

 

 

Coding standards

 특히 CPython에 포함시키기 위해 코드를 작성하는 경우 PEP7에 정의된 지침과 표준을 따르기를 권고한다.

 

Include Files

Python /C API를 사용하는 데 필요한 모든 함수, 형식 및 매크로 정의는 다음 코드에 포함되어 있다.

#define PY_SSIZE_T_CLEAN

#include <Python.h>

#define PY_SSIZE_T_CLEAN
#include <Python.h>

이것은 다음과 같은 표준 헤더를 포함한다는 것을 의미한다.

<stdio.h>, <string.h>,<errno.hl>,<limits.h>,<assert.h> and <stdlib.h> (가능한 경우)

파이썬은 일부 시스템에서 표준 헤더에 영향을 미치는 일부 사전 프로세서 정의를 정의 할 수  있으므로 표준 헤더가 포함되기 전에  Python.h를 포함시켜야한다. Python.h를 포함하기 전에 항상 PY_SSIZE_T_CLEAN를 정의하는 것이 좋다.

 

Python.h에 정의 된 모든 사용자 표시 이름 (포함 된 표준 헤더로 정의 된 이름 제외)은 접두어 Py 또는 _Py 중 하나를 가집니다. _Py로 시작하는 이름은 Python 구현에 의해 내부적으로 사용되며 확장 작성자가 사용해서는 안됩니다. 구조체 멤버 이름에는 예약 된 접두어가 없습니다.

 

중요 : 사용자 코드는 절대로 Py 또는 _Py로 시작하는 이름을 정의해서는 안됩니다. 이것은 독자를 혼란스럽게하고, 미래의 Python 버전으로의 사용자 코드의 이식성을 위태롭게합니다.이 버전은이 접두사 중 하나로 시작하는 추가 이름을 정의 할 수 있습니다.

중요 : 사용자 코드는 절대로 Py 또는 _Py로 시작하는 이름을 정의해서는 안됩니다. 
이것은 독자를 혼란스럽게하고, 미래의 Python 버전으로의 사용자 코드의 이식성을 위태롭게합니다.
이 버전은이 접두사 중 하나로 시작하는 추가 이름을 정의 할 수 있습니다.

헤더 파일은 대개 Python과 함께 설치됩니다. Unix에서는 prefix / include / pythonversion / 및 exec_prefix / include / pythonversion / 디렉토리에 있습니다. prefix 및 exec_prefix는 Python의 configure 스크립트에 해당 매개 변수로 정의되며 버전은 '% d. % d'% sys입니다. version_info [: 2]. Windows에서 머리글은 prefix / include에 설치되며, prefix는 설치 프로그램에 지정된 설치 디렉토리입니다.

 

헤더를 포함 시키려면 Include를 위해 컴파일러의 검색 경로에 두 디렉토리 (다른 경우)를 배치하십시오. 부모 디렉토리를 검색 경로에 두지 말고 #include ;를 사용하십시오. 접두어 아래의 플랫폼 독립 헤더에는 exec_prefix의 플랫폼 고유 헤더가 포함되므로 멀티 플랫폼 빌드에서 중단됩니다.

 

C++ 사용자는 API가 C를 사용하여 완전히 정의되었지만 헤더 파일이 엔트리 포인트를 extern "C"로 올바르게 선언하므로 C++에서 API를 사용하기 위해 특별한 작업을 수행 할 필요가 없다는 점에 유의해야합니다.

 

Useful macros

몇 가지 유용한 매크로는 Python 헤더 파일에 정의되어 있습니다. 많은 것은 유용한 곳 (예 : Py_RETURN_NONE)에 더 가깝게 정의됩니다. 보다 일반적인 유틸리티의 다른 것들은 여기에 정의되어 있습니다. 이것은 반드시 완전한 목록 일 필요는 없습니다.

 

## New in version 3.7.
Py_UNREACHABLE()
Use this when you have a code path that you do not expect to be reached.
For example, in the default: clause in a switch statement 
for which all possible values are covered in case statements.
Use this in places where you might be tempted to put an assert(0) or abort() call.


## New in version 3.3.
Py_ABS(x)
Return the absolute value of x.


## New in version 3.3.
Py_MIN(x, y)
Return the minimum value between x and y.


## New in version 3.3.
Py_MAX(x, y)
Return the maximum value between x and y.


## New in version 3.4.
Py_STRINGIFY(x)
Convert x to a C string. E.g. Py_STRINGIFY(123) returns "123".


## New in version 3.6.
Py_MEMBER_SIZE(type, member)
Return the size of a structure (type) member in bytes.


## New in version 3.4.
Py_CHARMASK(c)
Argument must be a character or an integer in the range [-128, 127] or [0, 255].
This macro returns c cast to an unsigned char.

Py_GETENV(s)
Like getenv(s), but returns NULL if -E was passed on the command line
(i.e. if Py_IgnoreEnvironmentFlag is set).

Py_UNUSED(arg)
Use this for unused arguments in a function definition to silence compiler warnings,
e.g. PyObject* func(PyObject *Py_UNUSED(ignored)).

Objects, Types and Reference Counts

대부분의 Python / C API 함수는 하나 이상의 인수와 PyObject * 유형의 반환 값을가집니다. 이 유형은 임의의 Python 오브젝트를 나타내는 은닉 데이터 형식에 대한 포인터입니다. 대부분의 상황 (예 : 할당, 범위 규칙 및 인수 전달)에서는 모든 Python 객체 유형이 Python 언어에서 동일한 방식으로 처리되기 때문에 단일 C 유형으로 표현되어야한다는 점이 적합합니다. 거의 모든 Python 객체가 힙에 있습니다. PyObject 유형의 자동 또는 정적 변수를 선언하지 않고 PyObject * 유형의 포인터 변수 만 선언 할 수 있습니다. 유일한 예외는 유형 객체입니다. 이것들은 결코 할당 해제되어서는 안되기 때문에, 일반적으로 정적 인 PyTypeObject 객체입니다.

 

모든 파이썬 객체 (파이썬 정수 까지도)는 타입과 참조 카운트를 가지고 있습니다. 객체의 유형에 따라 객체의 종류 (예 : 정수, 목록 또는 사용자 정의 함수)가 결정되며 표준 유형 계층 구조에서 설명 된 것보다 훨씬 많습니다. 각각의 잘 알려진 유형에 대해 객체가 그 유형인지 여부를 확인하는 매크로가 있습니다. 예를 들어, PyList_Check (a)는 a가 가리키는 객체가 파이썬리스트 인 경우에만 true입니다.

 

Reference Counts

참조 횟수는 오늘날의 컴퓨터가 한정된 (종종 심각하게 제한된) 메모리 크기를 가지기 때문에 중요합니다. 객체에 대한 참조가있는 곳의 수를 계산합니다. 그러한 장소는 다른 객체 또는 전역 (또는 정적) C 변수이거나 일부 C 함수의 로컬 변수 일 수 있습니다. 객체의 참조 횟수가 0이되면 객체는 할당이 해제됩니다. 다른 오브젝트에 대한 참조가 들어 있으면 참조 카운트가 감소합니다. 이러한 다른 객체는 참조 카운트가 0이되는 등의 감소가 발생하면 차례로 할당이 취소 될 수 있습니다. (여기서 서로를 참조하는 객체에는 명백한 문제가 있으며, 현재 해결책은 "그렇게하지 마십시오"입니다.)

 

참조 카운트는 항상 명시 적으로 조작됩니다. 일반적인 방법은 Py_INCREF () 매크로를 사용하여 객체의 참조 카운트를 1 씩 증가시키고 Py_DECREF ()를 사용하여 1 씩 감소시키는 것입니다. Py_DECREF () 매크로는 참조 횟수가 0이되었는지 여부를 확인한 다음 객체의 deallocator가 호출되도록해야하기 때문에 incrementf보다 훨씬 복잡합니다. deallocator는 객체의 유형 구조에 포함 된 함수 포인터입니다. 유형별 deallocator는 오브젝트와 같은 복합 오브젝트 유형 (예 : 목록) 일 경우 오브젝트에 포함 된 다른 오브젝트에 대한 참조 계수를 감소시키고 필요한 추가 완료를 수행합니다. 참조 횟수가 오버플로 될 가능성은 없습니다. 가상 메모리 (sizeof (Py_ssize_t)> = sizeof (void *)로 가정)에 다른 메모리 위치가 있으므로 참조 카운트를 보유하기 위해 적어도 많은 비트가 사용됩니다. 따라서, 참조 카운트 증가분은 간단한 연산이다.

 

객체에 대한 포인터를 포함하는 모든 지역 변수에 대해 객체의 참조 횟수를 증가시킬 필요는 없습니다. 이론적으로 객체의 참조 횟수는 변수를 가리킬 때 하나 씩 올라가고 변수가 범위를 벗어날 때 하나 씩 내려갑니다. 그러나 이 두 요소는 서로 취소되므로 결국에는 참조 횟수가 변경되지 않습니다. 참조 카운트를 사용하는 유일한 이유는 변수가 가리키는 한 객체의 할당이 해제되는 것을 방지하기 위해서입니다. 적어도 변수만큼 긴 객체에 대한 참조가 하나 이상 있다는 것을 알고 있다면 참조 카운트를 일시적으로 증가시킬 필요가 없습니다. 이 문제가 발생하는 중요한 상황은 Python에서 호출되는 확장 모듈의 C 함수에 인수로 전달되는 객체에 있습니다. 호출 메커니즘은 호출 기간 동안 모든 인수에 대한 참조를 보유하도록 보장합니다.

 

그러나 공통적 인 함정은 목록에서 객체를 추출하여 참조 카운트를 증가시키지 않으면서 잠시 동안 객체를 유지하는 것입니다. 다른 조작에 의해, 오브젝트를 리스트로부터 삭제해, 참조 카운트를 감소시켜, 오브젝트의 할당을 해제 할 가능성이 있습니다. 실제 위험은 무고한 동작이 이것을 할 수있는 임의의 파이썬 코드를 호출 할 수 있다는 것입니다. Py_DECREF ()에서 사용자에게 제어가 돌아가도록하는 코드 경로가 있으므로 거의 모든 작업이 잠재적으로 위험합니다.

 

안전한 접근법은 항상 일반적인 연산 (이름이 PyObject_, PyNumber_, PySequence_ 또는 PyMapping_으로 시작하는 함수)을 사용하는 것입니다. 이러한 연산은 항상 반환하는 객체의 참조 카운트를 증가시킵니다. 이로 인해 호출자는 Py_DECREF ()를 호출 할 책임이 있습니다. 이것은 곧 제 2의 기초가됩니다.

 

Reference Count Details

 

Python / 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"));

여기에서 PyLong_FromLong ()은 PyTuple_SetItem ()에 의해 즉시 도난당한 새로운 참조를 반환합니다. 객체에 대한 참조가 도난 당할지라도 객체를 계속 사용하려면 참조 도용 함수를 호출하기 전에 Py_INCREF ()를 사용하여 다른 참조를 가져옵니다.

 

덧붙여서, 튜플 항목을 설정하는 유일한 방법은 PyTuple_SetItem ()입니다. 튜플은 불변의 데이터 타입이기 때문에 PySequence_SetItem ()과 PyObject_SetItem ()은 이를 거부합니다. 자신을 생성하는 튜플에 대해서만 PyTuple_SetItem ()을 사용해야합니다.

 

리스트를 채우기위한 등가 코드는 PyList_New () 및 PyList_SetItem ()를 사용하여 기록 될 수있다.

 

그러나 실제로는 튜플이나 리스트를 만들고 채우는 이러한 방법을 거의 사용하지 않습니다. 일반적인 함수 인 Py_BuildValue ()는 C 값에서 가장 일반적인 객체를 생성 할 수 있으며 형식 문자열에 의해 지시됩니다. 예를 들어, 위의 두 코드 블록은 다음과 같이 바뀔 수 있습니다 (오류 검사도 처리됩니다).

 

PyObject *tuple, *list;

tuple = Py_BuildValue("(iis)", 1, 2, "three");
list = Py_BuildValue("[iis]", 1, 2, "three");

PyObject_SetItem ()과 friends가 참조하는 아이템을 빌려 쓰는 것, 즉 쓰고있는 함수에 전달 된 인수처럼 사용하는 것이 훨씬 더 일반적입니다. 이 경우 레퍼런스 카운트를 증가시킬 필요가 없기 때문에 레퍼런스 카운트에 대한 그들의 행동은 훨씬 건전합니다 ( "도난 당 했음"). 예를 들어이 함수는 목록의 모든 항목 (실제로는 변경 가능한 시퀀스)을 지정된 항목으로 설정합니다.

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 ()과 같은 객체 참조를 반환하는 제네릭 함수는 항상 새 참조를 반환합니다 (호출자는 참조의 소유자가됩니다).

 

함수에서 반환 된 참조를 소유하고 있는지 여부는 호출하는 함수에 따라 달라집니다. 즉, 함수에 인수로 전달 된 객체의 유형 인 plumage가 입력되지 않습니다. 따라서 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;
}

types

Python / C API에서 중요한 역할을하는 다른 데이터 유형은 거의 없습니다. 대부분 int, long, double, char *와 같은 단순한 C 타입이다. 몇 가지 구조 유형은 모듈에서 내보내는 함수 나 새 객체 유형의 데이터 속성을 나열하는 데 사용되는 정적 테이블을 설명하는 데 사용되며 다른 하나는 복소수의 값을 설명하는 데 사용됩니다. 이것들은 그것들을 사용하는 기능들과 함께 논의 될 것입니다.

 

EXceptions

Python 프로그래머는 특정 오류 처리가 필요한 경우 예외를 처리해야합니다. 처리되지 않은 예외는 자동으로 호출자로 전달 된 다음 호출자의 호출자에게 전파됩니다. 최상위 인터프리터에 도달하면 스택 추적 기능이 함께 사용자에게보고됩니다.

 

그러나 C 프로그래머의 경우 오류 검사는 항상 명시 적이어야합니다. 파이썬 / C API의 모든 함수는 함수의 문서에서 명시 적으로 주장하지 않는 한 예외를 발생시킬 수 있습니다. 일반적으로 함수는 오류가 발생하면 예외를 설정하고 소유하고있는 객체 참조를 버리고 오류 표시기를 반환합니다. 그렇지 않으면 문서화되지 않은 경우이 표시기는 함수의 반환 유형에 따라 NULL 또는 -1입니다. 몇 가지 함수는 부울 true / false 결과를 반환하고 false는 오류를 나타냅니다. 극히 일부 함수는 명시 적 오류 표시기를 반환하지 않거나 모호한 반환 값을 가지며 PyErr_Occurred ()로 오류를 명시 적으로 테스트해야합니다. 이러한 예외는 항상 명시 적으로 문서화됩니다

 

예외 상태는 스레드 단위 저장소에서 유지 관리됩니다 (스레드되지 않은 응용 프로그램에서 전역 저장소를 사용하는 것과 같습니다). 스레드는 두 가지 상태 중 하나 일 수 있습니다. 예외가 발생했는지 여부입니다. PyErr_Occurred () 함수는 이것을 확인하기 위해 사용할 수 있습니다. 예외가 발생하면 예외 유형 객체에 대한 빌린 참조를 반환하고 그렇지 않으면 NULL을 반환합니다. PyErr_SetString ()은 예외 상태를 설정하는 가장 일반적인 (가장 일반적인 것은 아니지만) 함수이며 PyErr_Clear ()는 예외 상태를 지 웁니다.

 

전체 예외 상태는 예외 유형, 해당 예외 값 및 역 추적이라는 세 개의 오브젝트 (모두 NULL 일 수 있음)로 구성됩니다. 이들은 sys.exc_info ()의 파이썬 결과와 같은 의미를가집니다. 그러나 파이썬 객체는 파이썬 try ... except 문에 의해 처리되는 마지막 예외를 나타내는 반면, C 수준 예외 상태는 예외가 C 함수간에 전달되는 동안 만 존재하며 파이썬 바이트 코드 인터프리터에 도달 할 때까지만 발생합니다 main 루프는 sys.exc_info ()와 친구들에게 전달합니다.

 

Python 1.5부터는 Python 코드에서 예외 상태에 액세스하는 기본 스레드 안전 방법은 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 ()를 사용하고, NULL 일 수있는 소유 참조를 처리하기 위해 Py_XDECREF ()를 사용합니다 (이름에 'X'가 표시되고 Py_DECREF ()가 충돌 할 때 충돌합니다) NULL 참조). 소유 된 참조를 유지하는 데 사용되는 변수가 작동하려면 NULL로 초기화하는 것이 중요합니다. 마찬가지로 제안 된 반환 값은 -1 (실패)으로 초기화되고 최종 호출이 성공한 후에 만 ​​성공으로 설정됩니다.

 

Embedding Python

파이썬 인터프리터의 임베디드 (확장 작성자와 반대되는)만이 고려해야 할 중요한 작업 중 하나는 파이썬 인터프리터의 초기화 및 마무리 작업이다. 인터프리터의 대부분의 기능은 인터프리터가 초기화 된 후에 만 ​​사용할 수 있습니다.

 

기본 초기화 함수는 Py_Initialize ()입니다. 이렇게하면로드 된 모듈의 테이블이 초기화되고 기본 모듈 인 builtins, __main__ 및 sys가 생성됩니다. 모듈 검색 경로 (sys.path)도 초기화합니다.

 

Py_Initialize ()는 "script argument list"(sys.argv)를 설정하지 않습니다. 이 변수가 나중에 실행될 Python 코드에서 필요하면 Py_Initialize ()를 호출 한 후 PySys_SetArgvEx (argc, argv, updatepath)를 호출하여 명시 적으로 설정해야합니다.

 

Py_Initialize ()는 Python 라이브러리가 발견되면 표준 Python 인터프리터 실행 파일의 위치를 ​​가장 잘 추측하여 모듈 탐색 경로를 계산합니다 (특히 Unix와 Windows에서는 세부 사항이 약간 씩 다르지만) Python 인터프리터 실행 파일에 상대적인 고정 된 위치에. 특히, 쉘 명령 검색 경로 (환경 변수 PATH)에 python이라는 실행 파일이있는 상위 디렉토리에 상대적인 lib / pythonX.Y 디렉토리를 찾습니다.

 

예를 들어, 파이썬 실행 파일이 / usr / local / bin / python에 있으면, 라이브러리는 /usr/local/lib/pythonX.Y에 있다고 가정합니다. 실제로이 특정 경로는 "폴백 (fallback)"위치이기도합니다. PATH를 따라 python이라는 실행 파일이 없을 때 사용됩니다. 사용자는 PYTHONHOME 환경 변수를 설정하여이 동작을 무시하거나 표준 앞에 추가 디렉토리를 삽입 할 수 있습니다 PYTHONPATH를 설정하여 경로를 지정하십시오.

 

임베디드 응용 프로그램은 Py_Initialize ()를 호출하기 전에 Py_SetProgramName (file)을 호출하여 검색을 조정할 수 있습니다. PYTHONHOME은 여전히 ​​이것을 덮어 쓰고 PYTHONPATH는 여전히 표준 경로 앞에 삽입됩니다. 완전한 제어가 필요한 어플리케이션은 Py_GetPath (), Py_GetPrefix (), Py_GetExecPrefix () 및 Py_GetProgramFullPath () (모듈 / getpath.c에 모두 정의 됨)의 자체 구현을 제공해야합니다.

 

때로는 파이썬을 "초기화하지 않는"것이 바람직합니다. 예를 들어, 응용 프로그램을 다시 시작하거나 (Py_Initialize ()를 다시 호출) 응용 프로그램을 Python을 사용하여 간단히 완료하고 Python에서 할당 한 메모리를 해제하고자 할 수 있습니다. 이것은 Py_FinalizeEx ()를 호출하여 수행 할 수 있습니다. 파이썬이 현재 초기화 된 상태라면 Py_IsInitialized () 함수는 참을 반환한다. 이 함수에 대한 자세한 내용은 다음 장에서 설명합니다. Py_FinalizeEx ()가 파이썬 인터프리터에 의해 할당 된 모든 메모리를 해제하지 않는다는 것에 주목하자. 현재 확장 모듈에 의해 할당 된 메모리는 해제 될 수 없습니다.

 

Debugging Builds

파이썬은 인터프리터와 확장 모듈을 추가로 검사 할 수 있도록 여러 개의 매크로로 만들 수 있습니다. 이러한 검사는 런타임에 많은 양의 오버 헤드를 추가하는 경향이 있으므로 기본적으로 활성화되지 않습니다.

 

디버깅 빌드의 다양한 유형의 전체 목록은 Python 소스 배포판의 Misc / SpecialBuilds.txt 파일에 있습니다. 참조 카운트 추적, 메모리 할당 자 디버깅 또는 기본 인터프리터 루프의 저수준 프로파일 링을 지원하는 빌드를 사용할 수 있습니다. 가장 자주 사용되는 빌드 만이이 섹션의 나머지 부분에서 설명됩니다.

 

인터프리터를 Py_DEBUG 매크로로 컴파일하면 일반적으로 파이썬의 "디버그 빌드"가 의미하는 바를 생성합니다. ./configure 명령에 --with-pydebug를 추가하여 Py_DEBUG를 Unix 빌드에서 사용할 수 있습니다. 또한 Python과 관련없는 _DEBUG 매크로가 있음을 암시합니다. Unix 빌드에서 Py_DEBUG가 활성화되면 컴파일러 최적화가 비활성화됩니다.

 

아래에 설명 된 참조 횟수 디버깅 외에도 다음과 같은 추가 검사가 수행됩니다.

 

  • Extra checks are added to the object allocator.
  • Extra checks are added to the parser and compiler.
  • Downcasts from wide types to narrow types are checked for loss of information.
  • A number of assertions are added to the dictionary and set implementations. In addition, the set object acquires a test_c_api() method.
  • Sanity checks of the input arguments are added to frame creation.
  • The storage for ints is initialized with a known invalid pattern to catch reference to uninitialized digits.
  • Low-level tracing and extra exception checking are added to the runtime virtual machine.
  • Extra checks are added to the memory arena implementation.
  • Extra debugging is added to the thread module.
  • 추가 검사는 객체 할당 자에 추가됩니다.
  • 추가 검사는 파서와 컴파일러에 추가됩니다.
  • 넓은 유형에서 좁은 유형으로의 다운 캐스트는 정보 손실을 검사합니다.
  • 많은 어설 션이 사전에 추가되고 구현을 설정합니다. 또한 set 객체는 test_c_api () 메서드를 가져옵니다.
  • 입력 인수의 온 전성 검사가 프레임 작성에 추가됩니다.
  • int의 기억 영역은 초기화되지 않은 숫자에 대한 참조를 catch하는 알려진 잘못된 패턴으로 초기화됩니다.
  • 저수준 추적 및 추가 예외 검사가 런타임 가상 시스템에 추가됩니다.
  • 추가 검사는 메모리 영역 구현에 추가됩니다.
  • 추가 디버깅이 스레드 모듈에 추가됩니다.

여기에 언급되지 않은 추가 검사가 있을 수 있읍니다.

 

Py_TRACE_REFS를 정의하면 참조 추적이 가능합니다. 정의 될 때, 순환 객체의 이중 연결리스트는 모든 PyObject에 2 개의 추가 필드를 추가함으로써 유지됩니다. 총 할당도 추적됩니다. 종료시 모든 기존 참조가 인쇄됩니다. (대화식 모드에서 이것은 인터프리터가 실행하는 모든 명령문이 실행 된 후에 발생합니다.) Py_DEBUG에 의해 묵시적으로 표현됩니다.

 

더 자세한 정보는 파이썬 소스 배포본의 Misc / SpecialBuilds.txt를 참고하십시오.

'my_lesson > _Python' 카테고리의 다른 글

Python - Django  (0) 2019.06.19
Python - C/C++ API Reference Manual Interface  (0) 2019.04.19
Python- C/C++ API Reference Manual Sequence  (0) 2019.04.19
Phyton - Django 설치 및 활용  (0) 2018.12.08
Python Tensorflow 설치  (0) 2018.10.06

댓글