# 확장/내장 FAQ

목차

- 확장/내장 FAQ

  - C로 나만의 함수를 만들 수 있습니까?

  - C++로 나만의 함수를 만들 수 있습니까?

  - C를 쓰는 것은 어렵습니다; 대안이 있습니까?

  - C에서 임의의 파이썬 문장을 어떻게 실행할 수 있습니까?

  - C에서 임의의 파이썬 표현식을 어떻게 평가할 수 있습니까?

  - 파이썬 객체에서 C값을 어떻게 추출합니까?

  - Py_BuildValue()를 사용하여 임의 길이의 튜플을 만드는 방법은 무엇입니까?

  - C에서 객체의 메서드를 어떻게 호출합니까?

  - PyErr_Print()의 출력(또는 stdout/stderr로 인쇄되는 모든 것)을 어떻게 잡습니까?

  - C에서 파이썬으로 작성된 모듈에 어떻게 액세스합니까?

  - 파이썬에서 C++ 객체에 어떻게 인터페이스 합니까?

  - Setup 파일을 사용하여 모듈을 추가했는데 make가 실패합니다; 왜 그렇습니까?

  - 확장을 어떻게 디버깅합니까?

  - 리눅스 시스템에서 파이썬 모듈을 컴파일하고 싶지만, 일부 파일이 없습니다. 왜 그렇습니까?

  - “잘못된 입력”과 “불완전한 입력”을 어떻게 구별할 수 있습니까?

  - 정의되지 않은 g++ 기호 __builtin_new나 __pure_virtual을 어떻게 찾을 수 있습니까?

  - 일부 메서드는 C로 구현되고 그 밖의 것은 파이썬으로 구현된 (예를 들어 상속을 통해) 객체 클래스를 만들 수 있습니까?

## C로 나만의 함수를 만들 수 있습니까?

그렇습니다. 함수, 변수, 예외 및 심지어 새로운 형을 포함하는 내장 모듈을 C로 만들 수 있습니다. 파이썬 인터프리터 확장 및 내장 문서에 설명되어 있습니다.

대부분의 중급이나 고급 파이썬 서적에서도 이 주제를 다룰 것입니다.

## C++로 나만의 함수를 만들 수 있습니까?

그렇습니다, C++에 있는 C 호환성 기능을 사용합니다. 파이썬 인클루드(include) 파일 주위에 `extern "C" { ... }`를 배치하고 파이썬 인터프리터가 호출할 각 함수 앞에 `extern "C"`를 배치하십시오. 생성자를 가진 전역이나 정적(static) C++ 객체는 대개 좋은 생각이 아닙니다.

## C를 쓰는 것은 어렵습니다; 대안이 있습니까?

수행하려는 작업에 따라, 여러분 만의 C 확장을 작성하는 여러 가지 대안이 있습니다.

Cython과 관련 Pyrex는 약간 수정된 파이썬 형식을 받아들이고 해당 C 코드를 생성하는 컴파일러입니다. Cython과 Pyrex를 사용하면 파이썬의 C API를 배우지 않고도 확장을 작성할 수 있습니다.

현재 파이썬 확장이 없는 일부 C나 C++ 라이브러리에 대한 인터페이스가 필요하면, 라이브러리의 데이터형과 함수를 SWIG과 같은 도구로 래핑할 수 있습니다. SIP, CXX, Boost 또는 Weave도 C++ 라이브러리 래핑의 대안입니다.

## C에서 임의의 파이썬 문장을 어떻게 실행할 수 있습니까?

이를 수행하는 최상위 수준 함수는 `PyRun_SimpleString()`이며, 이는 모듈 `__main__`의 컨텍스트에서 실행될 단일 문자열 인자를 취하고 성공하면 `0`을 반환하고 (`SyntaxError`를 포함하는) 예외가 발생하면 `-1`을 반환합니다. 더 많은 제어를 원하면, `PyRun_String()`을 사용하십시오; `Python/pythonrun.c`에 있는 `PyRun_SimpleString()` 소스를 참조하십시오.

## C에서 임의의 파이썬 표현식을 어떻게 평가할 수 있습니까?

이전 질문에서 나온 `PyRun_String()` 함수를 start 기호 `Py_eval_input`을 사용하여 호출하십시오; 표현식을 구문 분석하고, 평가하고 값을 반환합니다.

## 파이썬 객체에서 C값을 어떻게 추출합니까?

이는 객체의 형에 따라 다릅니다. 튜플이면, `PyTuple_Size()`는 길이를 반환하고 `PyTuple_GetItem()`은 지정된 인덱스의 항목을 반환합니다. 리스트는 비슷한 함수를 가지고 있습니다, `PyListSize()`와 `PyList_GetItem()`.

바이트열에서는, `PyBytes_Size()`는 길이를 반환하고 `PyBytes_AsStringAndSize()`는 값과 길이에 대한 포인터를 제공합니다. 파이썬 바이트열 객체는 널(null) 바이트를 포함할 수 있어서 C의 `strlen()`을 사용할 수 없음에 유의하십시오.

객체의 형을 검사하려면, 먼저 `NULL`이 아닌지 확인한 다음 `PyBytes_Check()`, `PyTuple_Check()`, `PyList_Check()` 등을 사용하십시오.

소위 ‘추상’ 인터페이스가 제공하는 파이썬 객체에 대한 고수준 API도 있습니다 – 자세한 내용은 `Include/abstract.h`를 읽으십시오. `PySequence_Length()`, `PySequence_GetItem()` 등과 같은 호출로 모든 종류의 파이썬 시퀀스와 인터페이스 할 수 있을 뿐만 아니라 숫자(`PyNumber_Index()` 등)와 PyMapping API의 매핑과 같은 다른 많은 유용한 프로토콜을 지원합니다.

## Py_BuildValue()를 사용하여 임의 길이의 튜플을 만드는 방법은 무엇입니까?

그럴 수 없습니다. 대신 `PyTuple_Pack()`을 사용하십시오.

## C에서 객체의 메서드를 어떻게 호출합니까?

`PyObject_CallMethod()` 함수는 객체의 임의의 메서드를 호출하는 데 사용할 수 있습니다. 매개 변수는 객체, 호출할 메서드의 이름, `Py_BuildValue()`에 사용되는 것과 같은 포맷 문자열 및 인자 값입니다:

    PyObject *
    PyObject_CallMethod(PyObject *object, const char *method_name,
                        const char *arg_format, ...);

메서드가 있는 모든 객체에서 작동합니다 – 내장이나 사용자 정의 모두 작동합니다. 반환 값을 `Py_DECREF()`할 책임은 여러분에게 있습니다.

예를 들어, 인자 10, 0으로 파일 객체의 “seek” 메서드를 호출하려면 (파일 객체 포인터가 “f”라고 가정합니다):

    res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
    if (res == NULL) {
            ... an exception occurred ...
    }
    else {
            Py_DECREF(res);
    }

`PyObject_CallObject()`는 항상 인자 목록에 대한 튜플을 원하므로, 인자 없이 함수를 호출하려면, format으로 “()”를 전달하고, 하나의 인자로 함수를 호출하려면, 인자를 괄호로 묶습니다, 예를 들어 “(i)”.

## PyErr_Print()의 출력(또는 stdout/stderr로 인쇄되는 모든 것)을 어떻게 잡습니까?

파이썬 코드에서, `write()` 메서드를 지원하는 객체를 정의하십시오. 이 객체를 `sys.stdout`과 `sys.stderr`에 대입하십시오. print_error를 호출하거나 표준 트레이스백 메커니즘이 작동하도록 두십시오. 그러면 출력은 여러분의 `write()` 메서드가 보내는 곳으로 갑니다.

이렇게 하는 가장 쉬운 방법은 `io.StringIO` 클래스를 사용하는 것입니다:

    >>> import io, sys
    >>> sys.stdout = io.StringIO()
    >>> print('foo')
    >>> print('hello world!')
    >>> sys.stderr.write(sys.stdout.getvalue())
    foo
    hello world!

같은 작업을 수행하는 사용자 정의 객체는 다음과 같습니다:

    >>> import io, sys
    >>> class StdoutCatcher(io.TextIOBase):
    ...     def __init__(self):
    ...         self.data = []
    ...     def write(self, stuff):
    ...         self.data.append(stuff)
    ...
    >>> import sys
    >>> sys.stdout = StdoutCatcher()
    >>> print('foo')
    >>> print('hello world!')
    >>> sys.stderr.write(''.join(sys.stdout.data))
    foo
    hello world!

## C에서 파이썬으로 작성된 모듈에 어떻게 액세스합니까?

다음과 같이 모듈 객체에 대한 포인터를 얻을 수 있습니다:

    module = PyImport_ImportModule("<modulename>");

모듈을 아직 임포트 하지 않았으면 (즉, `sys.modules`에 아직 없으면), 이것은 모듈을 초기화합니다; 그렇지 않으면 단순히 `sys.modules["<modulename>"]`의 값을 반환합니다. 이것은 모듈을 어떤 이름 공간에도 넣지 않음에 유의하십시오 – 단지 초기화되도록 하고 `sys.modules`에 저장되도록 합니다.

그런 다음, 다음과 같이 모듈의 어트리뷰트(즉 모듈에 정의된 모든 이름)에 액세스할 수 있습니다:

    attr = PyObject_GetAttrString(module, "<attrname>");

모듈에 있는 변수에 대입하기 위해 `PyObject_SetAttrString()`을 호출하는 것도 작동합니다.

## 파이썬에서 C++ 객체에 어떻게 인터페이스 합니까?

요구 사항에 따라 여러 가지 접근 방식이 있습니다. 이 작업을 수동으로 수행하려면, “확장 및 내장” 문서를 읽는 것으로 시작하십시오. 파이썬 런타임 시스템의 경우 C와 C++ 사이에는 큰 차이가 없다는 것을 상기하십시오 – 따라서 C 구조체 (포인터) 형을 중심으로 새로운 파이썬 형을 작성하는 전략이 C++ 객체에도 적용됩니다.

C++ 라이브러리의 경우, C를 쓰는 것은 어렵습니다; 대안이 있습니까?를 참조하십시오.

## Setup 파일을 사용하여 모듈을 추가했는데 make가 실패합니다; 왜 그렇습니까?

Setup은 개행으로 끝나야 하며, 개행이 없으면 빌드 프로세스가 실패합니다. (이 문제를 해결하려면 지저분한 셸 스크립트 해킹이 필요하며, 이 버그는 너무 사소해서 그런 노력을 들일만 한 가치가 없는 것 같습니다.)

## 확장을 어떻게 디버깅합니까?

동적으로 로드된 확장에 GDB를 사용할 때, 확장이 로드될 때까지 확장에 중단점을 설정할 수 없습니다.

`.gdbinit` 파일에서 (또는 대화식으로) 다음 명령을 추가하십시오:

    br _PyImport_LoadDynamicModule

그런 다음, GDB를 실행할 때:

    $ gdb /local/bin/python
    gdb) run myscript.py
    gdb) continue # repeat until your extension is loaded
    gdb) finish   # so that your extension is loaded
    gdb) br myfunction.c:50
    gdb) continue

## 리눅스 시스템에서 파이썬 모듈을 컴파일하고 싶지만, 일부 파일이 없습니다. 왜 그렇습니까?

대부분의 포장된 버전의 파이썬은 파이썬 확장을 컴파일하는 데 필요한 다양한 파일이 포함된 `/usr/lib/python2.x/config/` 디렉터리가 포함되어 있지 않습니다.

레드햇의 경우, 필요한 파일을 얻으려면 python-devel RPM을 설치하십시오.

데비안의 경우, `apt-get install python-dev`를 실행하십시오.

## “잘못된 입력”과 “불완전한 입력”을 어떻게 구별할 수 있습니까?

때로 파이썬 대화식 인터프리터의 동작을 흉내 내고 싶을 때가 있습니다. 이것은 입력이 불완전할 때 (예를 들어, “if” 문의 시작을 입력했거나 괄호나 삼중 문자열 따옴표를 닫지 않았을 때) 계속 프롬프트를 표시하지만, 입력이 유효하지 않으면 즉시 문법 에러 메시지를 표시합니다.

파이썬에서는 `codeop` 모듈을 사용할 수 있습니다. 이 모듈은 구문 분석기의 동작을 충분히 근사합니다. 예를 들어, IDLE은 이것을 사용합니다.

C에서 이렇게 하는 가장 쉬운 방법은 `PyRun_InteractiveLoop()`를 호출하고 (아마 별도의 스레드에서), 파이썬 인터프리터가 입력을 처리하도록 하는 것입니다. `PyOS_ReadlineFunctionPointer()` 가 여러분의 사용자 정의 입력 함수를 가리 키도록 설정할 수도 있습니다. 자세한 힌트는 `Modules/readline.c`와 `Parser/myreadline.c`를 참조하십시오.

그러나 때로는 나머지 응용 프로그램과 같은 스레드에서 내장된 파이썬 인터프리터를 실행해야 하고, 사용자 입력을 기다리는 동안 `PyRun_InteractiveLoop()`를 중지할 수 없습니다. 한 가지 해결책은 `PyParser_ParseString()`을 호출하고 `e.error`가 `E_EOF`와 같은지를 검사하는 것인데, 이는 입력이 불완전하다는 것을 의미합니다. 다음은 Alex Farber의 코드에서 영감을 얻은 테스트 되지 않은 샘플 코드 조각입니다:

    #define PY_SSIZE_T_CLEAN
    #include <Python.h>
    #include <node.h>
    #include <errcode.h>
    #include <grammar.h>
    #include <parsetok.h>
    #include <compile.h>

    int testcomplete(char *code)
      /* code should end in \n */
      /* return -1 for error, 0 for incomplete, 1 for complete */
    {
      node *n;
      perrdetail e;

      n = PyParser_ParseString(code, &_PyParser_Grammar,
                               Py_file_input, &e);
      if (n == NULL) {
        if (e.error == E_EOF)
          return 0;
        return -1;
      }

      PyNode_Free(n);
      return 1;
    }

다른 해결책은 수신된 문자열을 `Py_CompileString()`으로 컴파일하려고 하는 것입니다. 에러 없이 컴파일되면, `PyEval_EvalCode()`를 호출하여 반환된 코드 객체를 실행해 보십시오. 그렇지 않으면 나중을 위해 입력을 저장하십시오. 컴파일이 실패하면, 예외 튜플에서 메시지 문자열을 추출하고 이를 “unexpected EOF while parsing” 문자열과 비교하여 에러인지 단지 더 많은 입력이 필요한지를 확인하십시오. 다음은 GNU readline 라이브러리를 사용하는 완전한 예제입니다 (readline()을 호출하는 동안 SIGINT를 무시하고자 할 수 있습니다):

    #include <stdio.h>
    #include <readline.h>

    #define PY_SSIZE_T_CLEAN
    #include <Python.h>
    #include <object.h>
    #include <compile.h>
    #include <eval.h>

    int main (int argc, char* argv[])
    {
      int i, j, done = 0;                          /* lengths of line, code */
      char ps1[] = ">>> ";
      char ps2[] = "... ";
      char *prompt = ps1;
      char *msg, *line, *code = NULL;
      PyObject *src, *glb, *loc;
      PyObject *exc, *val, *trb, *obj, *dum;

      Py_Initialize ();
      loc = PyDict_New ();
      glb = PyDict_New ();
      PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());

      while (!done)
      {
        line = readline (prompt);

        if (NULL == line)                          /* Ctrl-D pressed */
        {
          done = 1;
        }
        else
        {
          i = strlen (line);

          if (i > 0)
            add_history (line);                    /* save non-empty lines */

          if (NULL == code)                        /* nothing in code yet */
            j = 0;
          else
            j = strlen (code);

          code = realloc (code, i + j + 2);
          if (NULL == code)                        /* out of memory */
            exit (1);

          if (0 == j)                              /* code was empty, so */
            code[0] = '\0';                        /* keep strncat happy */

          strncat (code, line, i);                 /* append line to code */
          code[i + j] = '\n';                      /* append '\n' to code */
          code[i + j + 1] = '\0';

          src = Py_CompileString (code, "<stdin>", Py_single_input);

          if (NULL != src)                         /* compiled just fine - */
          {
            if (ps1  == prompt ||                  /* ">>> " or */
                '\n' == code[i + j - 1])           /* "... " and double '\n' */
            {                                               /* so execute it */
              dum = PyEval_EvalCode (src, glb, loc);
              Py_XDECREF (dum);
              Py_XDECREF (src);
              free (code);
              code = NULL;
              if (PyErr_Occurred ())
                PyErr_Print ();
              prompt = ps1;
            }
          }                                        /* syntax error or E_EOF? */
          else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
          {
            PyErr_Fetch (&exc, &val, &trb);        /* clears exception! */

            if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
                !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
            {
              Py_XDECREF (exc);
              Py_XDECREF (val);
              Py_XDECREF (trb);
              prompt = ps2;
            }
            else                                   /* some other syntax error */
            {
              PyErr_Restore (exc, val, trb);
              PyErr_Print ();
              free (code);
              code = NULL;
              prompt = ps1;
            }
          }
          else                                     /* some non-syntax error */
          {
            PyErr_Print ();
            free (code);
            code = NULL;
            prompt = ps1;
          }

          free (line);
        }
      }

      Py_XDECREF(glb);
      Py_XDECREF(loc);
      Py_Finalize();
      exit(0);
    }

## 정의되지 않은 g++ 기호 __builtin_new나 __pure_virtual을 어떻게 찾을 수 있습니까?

g++ 확장 모듈을 동적으로 로드하려면, 파이썬을 다시 컴파일하고, g++를 사용하여 다시 링크하고 (파이썬 Modules Makefile에서 LINKCC를 변경하십시오), g++를 사용하여 여러분의 확장 모듈을 링크해야 합니다 (예를 들어, `g++ -shared -o mymodule.so mymodule.o`).

## 일부 메서드는 C로 구현되고 그 밖의 것은 파이썬으로 구현된 (예를 들어 상속을 통해) 객체 클래스를 만들 수 있습니까?

그렇습니다, `int`, `list`, `dict` 등과 같은 내장 클래스를 상속할 수 있습니다.

Boost 파이썬 라이브러리(BPL, http://www.boost.org/libs/python/doc/index.html)는 C++에서 이를 수행하는 방법을 제공합니다 (즉, BPL을 사용하여 C++로 작성된 확장 클래스를 상속할 수 있습니다).