Python-C를 사용한 확장 프로그래밍

C, C ++ 또는 Java와 같은 컴파일 된 언어를 사용하여 작성하는 모든 코드를 통합하거나 다른 Python 스크립트로 가져올 수 있습니다. 이 코드는 "확장자"로 간주됩니다.

Python 확장 모듈은 일반 C 라이브러리에 불과합니다. Unix 시스템에서 이러한 라이브러리는 일반적으로.so(공유 객체의 경우). Windows 시스템에서는 일반적으로.dll (동적으로 연결된 라이브러리의 경우).

확장 작성을위한 전제 조건

확장 프로그램 작성을 시작하려면 Python 헤더 파일이 필요합니다.

  • Unix 머신에서이를 위해서는 일반적으로 python2.5-dev 와 같은 개발자 별 패키지를 설치해야합니다 .

  • Windows 사용자는 바이너리 Python 설치 프로그램을 사용할 때 이러한 헤더를 패키지의 일부로 가져옵니다.

또한 C 프로그래밍을 사용하여 Python 확장을 작성하려면 C 또는 C ++에 대한 충분한 지식이 있다고 가정합니다.

Python 확장을 먼저 살펴 봅니다.

Python 확장 모듈을 처음 살펴 보려면 코드를 네 부분으로 그룹화해야합니다.

  • 헤더 파일 Python.h .

  • 모듈에서 인터페이스로 노출하려는 C 함수입니다.

  • Python 개발자가 확장 모듈 내의 C 함수에 표시하는 함수 이름을 매핑하는 테이블입니다.

  • 초기화 기능.

헤더 파일 Python.h

당신은 포함 할 필요 Python.h의 당신에게 내부 파이썬 API에 액세스 인터프리터에 모듈 후크 사용을 제공하여 C 소스 파일에 헤더 파일을.

필요한 다른 헤더 앞에 Python.h를 포함해야합니다. Python에서 호출하려는 함수와 함께 include를 따라야합니다.

C 함수

함수의 C 구현 시그니처는 항상 다음 세 가지 형식 중 하나를 취합니다.

static PyObject *MyFunction( PyObject *self, PyObject *args );

static PyObject *MyFunctionWithKeywords(PyObject *self,
                                 PyObject *args,
                                 PyObject *kw);

static PyObject *MyFunctionWithNoArgs( PyObject *self );

앞선 선언은 각각 Python 객체를 반환합니다. C에있는 것처럼 파이썬 에는 void 함수 같은 것이 없습니다 . 함수가 값을 반환하지 않도록하려면 파이썬의 C에 해당하는 C를 반환하십시오.None값. 파이썬 헤더는 우리를 위해 이것을 수행하는 Py_RETURN_NONE 매크로를 정의합니다.

C 함수의 이름은 확장 모듈 외부에서는 볼 수 없으므로 원하는대로 지정할 수 있습니다. 그것들은 정적 함수 로 정의됩니다 .

C 함수는 일반적으로 여기에 표시된대로 Python 모듈과 함수 이름을 함께 결합하여 명명됩니다.

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

모듈 모듈 안에있는 func 라는 파이썬 함수 입니다. 일반적으로 소스 코드에서 다음에 오는 모듈의 메서드 테이블에 C 함수에 대한 포인터를 넣을 것입니다.

메서드 매핑 테이블

이 메서드 테이블은 PyMethodDef 구조의 간단한 배열입니다. 그 구조는 다음과 같습니다.

struct PyMethodDef {
   char *ml_name;
   PyCFunction ml_meth;
   int ml_flags;
   char *ml_doc;
};

다음은이 구조의 멤버에 대한 설명입니다.

  • ml_name − 이것은 파이썬 프로그램에서 사용될 때 파이썬 인터프리터가 나타내는 함수의 이름입니다.

  • ml_meth − 이것은 이전 섹션에서 설명한 서명 중 하나를 가진 함수의 주소 여야합니다.

  • ml_flags − 이것은 ml_meth가 사용하고있는 세 가지 시그니처 중 인터프리터에게 알려줍니다.

    • 이 플래그는 일반적으로 METH_VARARGS 값을 갖습니다.

    • 함수에 키워드 인수를 허용하려는 경우이 플래그를 METH_KEYWORDS와 비트 단위 OR로 연결할 수 있습니다.

    • 인수를 허용하지 않음을 나타내는 METH_NOARGS 값을 가질 수도 있습니다.

  • ml_doc − 이것은 함수에 대한 독 스트링으로, 작성하고 싶지 않은 경우 NULL 일 수 있습니다.

이 테이블은 적절한 멤버에 대해 NULL과 0 값으로 구성된 센티넬로 종료되어야합니다.

위에서 정의한 함수의 경우 다음과 같은 메소드 매핑 테이블이 있습니다.

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

초기화 기능

확장 모듈의 마지막 부분은 초기화 함수입니다. 이 함수는 모듈이로드 될 때 Python 인터프리터에 의해 호출됩니다. 함수의 이름을 지정해야합니다.initModule, 여기서 Module모듈 의 이름입니다.

초기화 함수는 빌드 할 라이브러리에서 내 보내야합니다. Python 헤더는 PyMODINIT_FUNC를 정의하여 우리가 컴파일하는 특정 환경에서 발생하는 적절한 주문을 포함합니다. 함수를 정의 할 때 사용하기 만하면됩니다.

C 초기화 함수는 일반적으로 다음과 같은 전체 구조를 가지고 있습니다.

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

다음은 Py_InitModule3 함수에 대한 설명입니다.

  • func − 내보낼 기능입니다.

  • module_methods − 위에서 정의한 매핑 테이블 이름입니다.

  • docstring − 이것은 내선에서 제공하려는 설명입니다.

이 모든 것을 합치면 다음과 같습니다.

#include <Python.h>

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

위의 모든 개념을 사용하는 간단한 예-

#include <Python.h>

static PyObject* helloworld(PyObject* self) {
   return Py_BuildValue("s", "Hello, Python extensions!!");
}

static char helloworld_docs[] =
   "helloworld( ): Any message you want to put here!!\n";

static PyMethodDef helloworld_funcs[] = {
   {"helloworld", (PyCFunction)helloworld, 
      METH_NOARGS, helloworld_docs},
      {NULL}
};

void inithelloworld(void) {
   Py_InitModule3("helloworld", helloworld_funcs,
                  "Extension module example!");
}

여기서 Py_BuildValue 함수는 Python 값을 빌드하는 데 사용됩니다. 위의 코드를 hello.c 파일에 저장합니다. Python 스크립트에서 호출 할이 모듈을 컴파일하고 설치하는 방법을 살펴 보겠습니다.

확장 빌드 및 설치

의 distutils의 패키지는 매우 쉽게 표준 방식으로, 파이썬 모듈, 모두 순수 파이썬과 확장 모듈을 배포 할 수 있습니다. 모듈은 소스 형식으로 배포되며 일반적 으로 다음과 같이 setup.py 라는 설정 스크립트를 통해 빌드 및 설치 됩니다.

위 모듈의 경우 다음 setup.py 스크립트를 준비해야합니다.

from distutils.core import setup, Extension
setup(name='helloworld', version='1.0',  \
      ext_modules=[Extension('helloworld', ['hello.c'])])

이제 올바른 컴파일러 및 링커 명령 및 플래그를 사용하여 필요한 모든 컴파일 및 링크 단계를 수행하고 결과 동적 라이브러리를 적절한 디렉토리에 복사하는 다음 명령을 사용합니다.

$ python setup.py install

Unix 기반 시스템에서 site-packages 디렉토리에 쓸 수있는 권한을 가지려면이 명령을 루트로 실행해야 할 가능성이 높습니다. 이것은 일반적으로 Windows에서 문제가되지 않습니다.

확장 가져 오기

확장을 설치하면 다음과 같이 Python 스크립트에서 해당 확장을 가져 와서 호출 할 수 있습니다.

#!/usr/bin/python
import helloworld

print helloworld.helloworld()

이것은 다음 결과를 생성합니다-

Hello, Python extensions!!

함수 매개 변수 전달

인수를받는 함수를 정의하고 싶을 가능성이 높으므로 C 함수에 다른 서명 중 하나를 사용할 수 있습니다. 예를 들어, 몇 개의 매개 변수를받는 다음 함수는 다음과 같이 정의됩니다.

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Parse args and do something interesting here. */
   Py_RETURN_NONE;
}

새 함수에 대한 항목을 포함하는 메소드 테이블은 다음과 같습니다.

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { "func", module_func, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
};

API PyArg_ParseTuple 함수를 사용하여 C 함수에 전달 된 하나의 PyObject 포인터에서 인수를 추출 할 수 있습니다 .

PyArg_ParseTuple의 첫 번째 인수는 args 인수입니다. 이것이 구문 분석 할 개체 입니다. 두 번째 인수는 나타날 것으로 예상되는 인수를 설명하는 형식 문자열입니다. 각 인수는 다음과 같이 형식 문자열에서 하나 이상의 문자로 표시됩니다.

static PyObject *module_func(PyObject *self, PyObject *args) {
   int i;
   double d;
   char *s;

   if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
      return NULL;
   }
   
   /* Do something interesting here. */
   Py_RETURN_NONE;
}

모듈의 새 버전을 컴파일하고 가져 오면 모든 유형의 인수로 새 함수를 호출 할 수 있습니다.

module.func(1, s="three", d=2.0)
module.func(i=1, d=2.0, s="three")
module.func(s="three", d=2.0, i=1)

아마도 더 많은 변형을 생각 해낼 수 있습니다.

PyArg_ParseTuple의 기능

다음은 표준 서명입니다. PyArg_ParseTuple 기능-

int PyArg_ParseTuple(PyObject* tuple,char* format,...)

이 함수는 오류에 대해 0을 반환하고 성공에 대해 0이 아닌 값을 반환합니다. 튜플은 C 함수의 두 번째 인수 인 PyObject *입니다. 여기서 형식 은 필수 및 선택적 인수를 설명하는 C 문자열입니다.

다음은 형식 코드 목록입니다. PyArg_ParseTuple 기능-

암호 C 유형 의미
길이가 1 인 파이썬 문자열은 C 문자가됩니다.
더블 Python float는 C double이됩니다.
에프 흙손 Python float는 C float가됩니다.
나는 int Python int는 C int가됩니다.
Python int는 C long이됩니다.
오래 오래 Python int는 C long long이됩니다.
영형 PyObject * Python 인수에 대한 NULL이 아닌 빌린 참조를 가져옵니다.
에스 숯* C char *에 포함 된 null이없는 Python 문자열입니다.
에스# char * + int C 주소 및 길이에 대한 모든 Python 문자열.
티# char * + int C 주소 및 길이에 대한 읽기 전용 단일 세그먼트 버퍼.
Py_UNICODE * C에 null이 포함되지 않은 Python 유니 코드.
유# Py_UNICODE * + int 모든 Python 유니 코드 C 주소 및 길이.
w # char * + int 단일 세그먼트 버퍼를 C 주소 및 길이로 읽고 씁니다.
숯* s와 마찬가지로 None도 허용합니다 (C char *를 NULL로 설정).
지# char * + int s #과 마찬가지로 None도 허용합니다 (C char *를 NULL로 설정).
(...) 당 ... Python 시퀀스는 항목 당 하나의 인수로 처리됩니다.
|   다음 인수는 선택 사항입니다.
:   형식 끝과 오류 메시지의 함수 이름을 차례로 지정합니다.
;   끝 형식을 지정하고 그 뒤에 전체 오류 메시지 텍스트를 표시합니다.

반환 값

Py_BuildValuePyArg_ParseTuple 과 매우 유사한 형식 문자열을 받습니다 . 구축중인 값의 주소를 전달하는 대신 실제 값을 전달합니다. 다음은 추가 기능을 구현하는 방법을 보여주는 예입니다.

static PyObject *foo_add(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("i", a + b);
}

이것이 파이썬으로 구현 된 경우의 모습입니다-

def add(a, b):
   return (a + b)

다음과 같이 함수에서 두 개의 값을 반환 할 수 있습니다.이 값은 Python의 목록을 사용하여 캐업됩니다.

static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("ii", a + b, a - b);
}

이것이 파이썬으로 구현 된 경우의 모습입니다-

def add_subtract(a, b):
   return (a + b, a - b)

Py_BuildValue의 기능

다음은 표준 서명입니다. Py_BuildValue 기능-

PyObject* Py_BuildValue(char* format,...)

여기 형식 은 빌드 할 Python 개체를 설명하는 C 문자열입니다. Py_BuildValue 의 다음 인수 는 결과가 빌드되는 C 값입니다. 의 PyObject *의 결과는 새로운 기준이다.

다음 표에는 일반적으로 사용되는 코드 문자열이 나열되어 있으며이 중 0 개 이상이 문자열 형식으로 결합됩니다.

암호 C 유형 의미
AC 문자는 길이가 1 인 Python 문자열이됩니다.
더블 AC double은 Python float가됩니다.
에프 흙손 AC float는 Python float가됩니다.
나는 int AC int는 Python int가됩니다.
AC long은 Python int가됩니다.
PyObject * Python 객체를 전달하고 참조를 훔칩니다.
영형 PyObject * Python 객체를 전달하고 정상적으로 INCREF합니다.
영형& 변환 + 무효 * 임의 변환
에스 숯* C 0으로 끝나는 char *는 Python 문자열, NULL은 None입니다.
에스# char * + int C char * 및 Python 문자열의 길이 또는 NULL에서 없음.
Py_UNICODE * C-wide, null로 끝나는 문자열을 Python 유니 코드로, 또는 NULL을 없음.
유# Py_UNICODE * + int C-wide 문자열과 길이는 Python 유니 코드로, NULL은 None입니다.
w # char * + int 단일 세그먼트 버퍼를 C 주소 및 길이로 읽고 씁니다.
숯* s와 마찬가지로 None도 허용합니다 (C char *를 NULL로 설정).
지# char * + int s #과 마찬가지로 None도 허용합니다 (C char *를 NULL로 설정).
(...) 당 ... C 값에서 Python 튜플을 빌드합니다.
[...] 당 ... C 값에서 Python 목록을 작성합니다.
{...} 당 ... 키와 값을 번갈아 가며 C 값에서 Python 사전을 빌드합니다.

코드 {...}는 짝수의 C 값 (교대로 키와 값)에서 사전을 빌드합니다. 예를 들어 Py_BuildValue ( "{issi}", 23, "zig", "zag", 42)는 Python의 {23 : 'zig', 'zag': 42}와 같은 사전을 반환합니다.