앞으로    목차    1    2    3    4    제5장    6    7    8    다음으로

5. 파이썬 확장하기

5.1. C로 직접 함수를 작성할 수 있나요?

네, 내장 모듈을 만들 수 있습니다. 함수, 변수, 예외 그리고 심지어는 새로운 형을 C로 내장 모듈에 담을 수 있습니다. 이것은 문서 "파이썬 인터프리터를 내장하고 확장하기 (Extending and Embedding the Python Interpreter)"에 설명되어 있습니다. 또 동적 적재에 관한 장도 읽어 보세요.

이것에 대한 더 많은 정보는 파이썬에 관한 책들에 있습니다: 프로그래밍 파이썬, 파이썬으로 인터넷 프로그래밍하기(Programming Python, Internet Programming with Python), 그리고 파이썬-책(Das Python-Buch) (독일어).


5.2. C++로 직접 함수를 작성할 수 있나요?

그렇습니다, C++에서 발견되는 C-호환성을 사용해서요. 기본적으로 extern "C" { ... }를 파이썬의 include 파일의 주위에 둘러싸고 extern "C" 를 각 함수 앞에 배치하면 파이썬 인터프리터에 의해서 호출될 것입니다. 전역 혹은 C++ 객체를 구성자와 함께 사용하는 것은 별로 좋은 생각이 아닙니다.


5.3. 임의의 파이썬 서술문을 C로부터 어떻게 실행할 수 있나요?

이렇게 하기 위한 최고-수준의 함수는 PyRun_SimpleString()로서 한개의 문자열 인수를 취하는데 __main__ 모듈의 문맥에서 실행되어 (구문에러를 포함하여) 예외가 일어나면 -1을 성공하면 0을 반환합니다. 더 제어를 하고 싶으면, PyRun_String()을 사용하세요; Python/pythonrun.c에 있는 PyRun_SimpleString()을 참조하세요.


5.4. 임의의 파이썬 표현식을 어떻게 C로부터 평가할 수 있나요?

이전 질문에 있었던 함수 PyRun_String()을 시작 심볼 eval_input을 가지고 호출하세요 ( 1.5a1에서는 Py_eval_input으로 시작합니다); 그것은 표현식을 해석해서, 평가하고, 그리고 그 값을 반환합니다.


5.5. 어떻게 파이썬 객체로부터 C 값을 추출하나요?

그것은 그 객체의 형에 따라 다릅니다. 터플이라면, PyTupleSize(o)는 길이를 반환하고 PyTuple_GetItem(o, i)는 i번째의 항목을 반환합니다; 리스트의 PyListSize(o)와 PyList_GetItem(o, i)도 비슷합니다. 문자열에 관해서는, PyString_Size(o) 그 길이를, PyString_AsString(o)는 그 값에 대한 포인터를 반환합니다 (주목할 것은 파이썬 문자열은 null 바이트를 포함할 수 있어서 strlen()은 안전하지 않습니다). 객체가 어떤 형인지 테스트해보기 위해서는, 먼저 그것이 NULL이 아니라는 것을 확인하세요, 그리고 나서 PyString_Check(o), PyTuple_Check(o), PyList_Check(o), 등등을 사용하세요.

또한 파이썬 객체에 대한 고-수준 API도 있는데 이른바 '추상적(abstract)' 인터페이스에 의하여 제공됩니다 -- 더 자세한 사항은 Include/abstract.h를 읽어 보세요. 예를 들어 그것은 (PySequence_Length(), PySequence_GetItem(), 등등과 같은 호출을 사용하여) 어떠한 파이썬 연속열(예. 리스트와 터플)과의 접속도 허용할 뿐만 아니라 다른 많은 유용한 프로토콜과의 접속을 허용합니다.


5.6. Py_BuildValue()를 사용하여 어떻게 임의의 길이를 가진 터플을 생성하나요?

그럴수 없습니다. 대신에 t = PyTuple_New(n)를 사용하세요, 그리고 그것을 PyTuple_SetItem(t, i, o)을 사용하는 객체로 채우세요 -- note that this "eats" a reference count of o. 리스트의 PyList_New(n)와 PyList_SetItem(l, i, o)에도 비슷하게 적용됩니다 . 주의할 것은 반드시 모든 터플 항목을 그 터플을 파이썬 코드에 건네기 전에 어떤 값으로 설정해야 한다는 것입니다. -- PyTuple_New(n)는 그것들을 NULL로 초기화 하는데, 그 값은 유효한 파이썬 값이 아닙니다.


5.7. 객체의 메쏘드를 C로부터 어떻게 호출하나요?

한 객체의 임의적인 메쏘드를 호출하는데에는 PyObject_CallMethod() 함수가 사용될 수 있습니다. 매개변수들은 그 객체와, 호출될 메쏘드의 이름, Py_BuildValue()와 사용되는 것과 같은 형식화 문자열 등등입니다, 그리고 그 인수 값들은:

    PyObject *
    PyObject_CallMethod(PyObject *object, char *method_name,
                        char *arg_format, ...);
이것은 -- 내장이든 혹은 사용자-정의이든 메쏘드를 가지고 있는 모든 객체에 작동합니다. 그 반환값의 참조횟수를 감소시키는(DECREF'ing) 것은 결국 여러분의 책임입니다.

예를 들어 한 파일 객체의 "seek" 메쏘드를 인수 10, 0 을 가지고 호출하려면 (그 파일 객체의 포인터가 "f"라는 가정하에):

        res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
        if (res == NULL) {
                ... an exception occurred ...
        }
        else {
                DECREF(res);
        }
주목할 것은 PyObject_CallObject()가 항상 그 인수 목록에 대하여 터플을 원하므로, 함수를 인수없이 호출하려면, 그 형식에 "()"를 건네 주세요, 그리고 함수를 하나의 인수로 호출하려면, 그 인수를 괄호로 둘러싸세요, 다음과 같이 "(i)".


5.8. PyErr_Print()가 내는 출력을 어떻게 나포하나요 (혹은 stdout/stderr에 출력되는 모든 것)?

(마크 해몬드의 (Mark Hammond)의 배려):

파이썬 코드에서는, "write()" 메쏘드를 지원하는 객체를 선언하세요. sys.stdout와 sys.stderr를 이 객체에 방향전환하세요. print_error를 호출하거나, 혹은 단순히 표준 역추적 메카니즘이 작동하도록 허용하세요. 그러면, 그 출력은 여러분의 write() 메쏘드가 그것을 보내는 곳에 출력될 것입니다.

이렇게 하는 가장 쉬운 방법은 표준 라이브러리에 있는 StringIO 클래스를 사용하는 것입니다.

표준 출력을 나포하기 위한 예제 코드와 사용법:

	>>> class StdoutCatcher:
	...  def __init__(self):
	...   self.data = ''
	...  def write(self, stuff):
	...   self.data = self.data + stuff
	...
	>>> import sys
	>>> sys.stdout = StdoutCatcher()
	>>> print 'foo'
	>>> print 'hello world!'
	>>> sys.stderr.write(sys.stdout.data)
	foo
	hello world!


5.9. 파이썬으로 작동된 모듈에 C에서 어떻게 접근하나요?

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

        module = PyImport_ImportModule("<modulename>");
만약 그 모듈이 아직 수입되지 않았다면 (즉. sys.modules에 아직 존재하지 않는다면), 이것은 그 모듈을 초기화합니다; 그렇지 않으면 단순히 sys.modules["<modulename>"]의 그 값을 반환합니다. 주목할 것은 그 모듈을 어떠한 이름공간에도 집어 넣지 않는다는 것입니다 -- 오직 초기화 되었는지 그리고 sys.modules에 저장되었는지 만을 확인합니다.

그러면 그 모듈의 속성에 (즉. 그 모듈에 정의된 어떤 이름에도) 다음과 같이 접근할 수 있습니다:

        attr = PyObject_GetAttrString(module, "<attrname>");
변수들을 그 모듈에 할당하기 위하여, PyObject_SetAttrString()을 호출하는 것 또한 작동합니다.


5.10. 어떻게 파이썬으로부터 C++ 객체에 접속하나요?

필요에 따라, 많은 접근방법이 있습니다. 수동으로 이렇게 하기 위해서는, "Extending and Embedding" 문서를 읽음으로서 시작하세요 (Doc/ext.tex, 또 다음 참조 http://www.python.org/doc/). 알수 있는 것은 파이썬의 실행-시간 시스템에 대하여, C와 C++의 사이에는 전적으로 많은 차이가 있지는 않다는 것입니다 -- 그래서 C의 구조(포인터) 형에서 새로운 파이썬 형을 구축하기 위한 전략은 C++ 객체에도 작동할 것입니다.

유용한 자동화된 접근법은 SWIG입니다 (C에도 작동합니다): http://www.swig.org//.


5.11. mSQLmodule이 (혹은 구형 모듈이) 파이썬 1.5 (혹은 그이상)으로 구축되지 않습니다.

파이썬 1.4 이후로 "Python.h"가 확장모듈에서 필요한 include 파일을 가질 것입니다. 하위 호환성은 버전 1.4이후로 떨어집니다. 그래서 mSQLmodule.c 은 "allobjects.h"를 발견할 수 없으므로 구축되지 않을 것입니다. mSQLmodule.c에서의 다음의 변경은 1.4버전으로 구축할 때는 무해하며 그리고 이후의 파이썬 버전에서 그렇게 하려면 필수적입니다:

다음 라인을 지우고:

	#include "allobjects.h"
	#include "modsupport.h"
대신에 다음을 삽입하세요:

	#include "Python.h"
다음과 같이 추가할 필요도 있습니다.

                #include "rename2.h"
만약 그 모듈이 "old names"을 사용하면.

이것은 다른 오래된 파이썬 모듈에도 일어날 수 있는데, 같은 해결책이 적용됩니다.


5.12. 설정 파일을 사용하여 모듈을 추가하였습니다 그런데 make 가 실패합니다! 어찌된 일이지요?

설정은 newline으로 끝나야 하는데, 만약 newline이 없다면 슬픔을 맞봅니다. 이 가능성을 제쳐두고, 아마도 다른 비-파이썬-종속적인 연결 문제를 가지고 있을 수도 있습니다.


5.13. 파이썬 모듈을 레드해트 리눅스 시스템에서 컴파일하려고 합니다, 그러나 어떤 파일들이 분실되어 없습니다.

파이썬을 위한, 레드 해트의 RPM은 /usr/lib/python1.x/config/ 디렉토리를 포함하고 있지 않은데, 그 디렉토리는 파이썬 확장을 컴파일하는데 필요한 다양한 파일들을 담고 있습니다. 파이썬-개발(python-devel) RPM을 설치하셔서 필요한 파일들을 얻도록 하세요.


5.14. "SystemError: _PyImport_FixupExtension: module yourmodule not loaded" 이것이 무슨 뜻인가요?

이것은 "yourmodule"이라는 이름의 확장 모듈을 생성하였지만, 모듈의 초기화 함수가 그 이름으로 초기화하지 않는다는 뜻입니다.

모든 모듈의 초기화 함수는 다음과 비슷한 라인을 가질 것입니다:

  module = Py_InitModule("yourmodule", yourmodule_functions);
만약 이 함수에 건네지는 문자열이 여러분의 확장 모듈 이름과 같지 않다면, SystemError가 일어날 것입니다.


5.15. "미완성 입력"과 "무효한 입력"을 구분하는 법?

가끔 여러분은 파이썬의 상호대화적인 행위를 흉내내기를 원하는데, 입력인 미완성 일때는 계속적으로 프롬프트를 보여 주지만 (예를 들어. "if" 서술문의 처음을 타자하거나 괄호 혹은 삼중 문자열 인용부호를 닫지 않을 때), 그 입력이 무효할 때는 즉시 구문 에러 메시지를 보여줍니다.

파이썬에서는 codeop 모듈을 사용할 수 있는데, 해석기의 행위를 거의 근접하게 흉내냅니다. 예를 들어, IDLE은 이것을 사용합니다.

C에서 그렇게 하기위한 가장 쉬운 방법은 PyRun_InteractiveLoop()를 호출하고 (아마도 개별적인 쓰레드로) 그리고 파이썬 인터프리터가 그 입력을 여러분을 대신하여 처리하도록 놓아 두는 것입니다. 또한 PyOS_ReadlineFunctionPointer를 설정하여 자신이 재단한 입력 함수를 지적할 수도 있습니다. 더 힌트를 원하시면 Modules/readline.c와 Parser/myreadline.c를 참조하세요.

그렇지만 때로 내장된 파이썬 인터프리터를 여러분의 나머지 어플리케이션과 같은 쓰레드에서 실행할 필요가 있지만, 사용자 입력을 기다리는 중이어서 PyRun_InteractiveLoop()를 중지 시킬수 없는 때가 있습니다. 그러면 하나의 해결책은 PyParser_ParseString()를 호출해서 e.error가 E_EOF와 같은지 테스트하는 것입니다 (이때 그 입력은 미완성입니다). 다음은 알렉스 파버(Alex Farber)의 코드에 영감을 받은, 검증되지 않은, 예제 코드 조각입니다:

  #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()를 호출함으로써 실행해 보세요. 그렇지 않으면 나중에 사용하기 위하여 그 입력을 저장하세요. 만약 컴파일이 실패하면, 그것이 에러인지 혹은 단지 더 입력이 필요한 것인지 알아 보세요 - 그 예외 터플로부터 메시지 문자열을 추출하여 그것을 "해석중에 예상 못한 EOF(unexpected EOF while parsing)"과 비교해 보시면 됩니다. 여기에 GNU readline 라이브러리를 사용한 완전한 예제가 있습니다. (readline()호출하는 중에 SIGINT를 무시하고 싶을 수도 있습니다):

  #include <stdio.h>
  #include <readline.h>
  #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 ((PyCodeObject *)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);
  }


5.16. 어떻게 확장을 디버그하나요?

gdb를 동적으로 적재된 확장과 사용할 때, 확장이 적재되기 전까지는 정지점을 설정할 수 없습니다.

.gdbinit 파일에서 (혹은 상호대화적으로), 명령어를 추가하세요

br _PyImport_LoadDynamicModule

$ 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


5.17. 정의되지 않은, 리눅스의 g++ 심볼을 어떻게 발견하나요, __builtin_new 또는 __pure_virtural

동적으로 g++ 확장 모듈을 적재하려면, 파이썬을 다시 컴파일하고, g++을 사용하여 다시 연결해야 합니다 (파이썬 모듈의 Makefile에 있는 LINKCC를 변경하세요 change LINKCC in the python Modules Makefile), 그리고 여러분의 확장 모듈을 g++을 사용하여 연결해야 합니다 (예를 들어, "g++ -shared -o mymodule.so mymodule.o").


5.18. 내장/확장 형들에 상응하는 객체들을 어떻게 정의하고 생성합니까?

보통 이런 질문을 던질때는 파이썬 형으로부터 상속을 받고 싶어서일 것입니다. 파이썬을 위한 최소한의 예견할 수 있는 미래는: 형과 클래스는 섞일 수 없습니다. 클래스를 호출하여 실체를 구축하고, 여러분의 뜻대로 하부클래스를 구축할 수 있습니다.

그렇지만, 자바와 마찬가지로, 파이썬은 제일-클래스(first-class) 객체와 제이-클래스(second-class) 객체가 있다고 주장합니다 (전자는 형이고, 후자는 클래스입니다), 그리고 그 두 쌍동이는 절대로 만나지 않을 것입니다[아마도].

그렇지만, 라이브러리는 더 일반적으로 요망되는 객체들을 위한 클래스 포장자를 훌륭하게 제공하여 왔으며 (예제를 보시려면 UserDict, UserList 그리고 UserString을 참조하세요), 그리고 여러분이 혹시나 코드를 작성하고 싶은 기분이 든다면 언제나 환영하고 있습니다.


앞으로    목차    1    2    3    4    제5장    6    7    8    다음으로