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_BuildValue 는 PyArg_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}와 같은 사전을 반환합니다.