Python 의 C 확장 - 학습 총화

12832 단어 python 진급
1. Python 의 C 확장
사실 C 언어 프로 그래 밍 을 알 면 Python 에 새로운 내장 (build - in) 모듈 을 추가 하 는 것 이 쉬 울 것 입 니 다.이 확장 (extension) 모듈 은 Python 에서 직접 진행 할 수 없 는 두 가지 조작 을 실현 할 수 있 습 니 다. 그들 은 새로운 내 장 된 대상 유형 과 C 언어 를 호출 할 수 있 는 라 이브 러 리 함수 와 시스템 호출 을 실현 할 수 있 습 니 다.
확장 을 지원 하기 위해 Python API 는 함수 (functions), 매크로 (macros) 명령 과 변수 (variables) 의 집합 을 정의 합 니 다. 이 집합 은 Python 이 실 행 될 때 (run - time) 시스템 에 대한 다각적 인 접근 을 제공 합 니 다.Python API 는 헤더 파일 Python. h 를 포함 하 는 방식 으로 C 언어 원본 파일 에 통합 할 수 있 습 니 다.
메모: C 언어의 확장 인 터 페 이 스 는 CPython 을 가리 키 며 확장 모듈 은 다른 Python 에서 유효 하지 않 습 니 다.많은 경우 C 확장 을 피하 고 이식 성 을 다른 실현 에 유지 할 수 있 습 니 다.예 를 들 어 C 라 이브 러 리 함 수 를 사용 하거나 시스템 호출 을 한다 면 사용자 정의 C 코드 대신 ctypes 모듈 이나 cffi 라 이브 러 리 를 사용 하 는 것 을 고려 할 수 있 습 니 다.이 모듈 들 은 C 코드 와 연결 하기 위해 Python 코드 를 작성 할 수 있 으 며, Python 은 C 확장 모듈 을 작성 하고 컴 파일 하 는 것 보다 더 편리 합 니 다.A Simple Example
스 팸 이라는 확장 모듈 을 구축 하고 C 라 이브 러 리 함수 system () 에 Python 인 터 페 이 스 를 만 들 고 싶다 고 가정 합 니 다.이 함 수 는 null 끝 (null - terminated) 문자열 을 매개 변수 로 하고 정 수 를 되 돌려 줍 니 다.우 리 는 이 함수 가 다음 형식 을 통 해 Python 에서 호출 될 수 있 기 를 바 랍 니 다.
import spam status = spam. system ("ls - l") 우선 spammodule. c 파일 을 새로 만 듭 니 다.(일반적으로 한 모듈 의 이름 이 spam 이 라면 C 언어 파일 을 spammodule. c 라 고 명명 해 야 합 니 다. 모듈 의 이름 이 매우 길 면 spammify 와 같 으 면 해당 하 는 파일 이름 을 spammify. c 라 고 직접 쓸 수 있 습 니 다.)
이 파일 의 첫 번 째 행동:
#include 

이것 은 Python API 를 가 져 올 수 있 습 니 다.
메모: Python 은 예비 프로세서 의 정 의 를 정의 합 니 다. 이 정 의 는 일부 시스템 의 표준 헤더 파일 에 영향 을 줄 수 있 기 때문에 파일 의 처음부터 Python. h 파일 을 포함 해 야 합 니 다.그리고 함수 의 주체 코드 를 추가 합 니 다:
static PyObject * spam_system(PyObject *self, PyObject *args)  
{  
    const char *command;  
    int sts;  
    if (!PyArg_ParseTuple(args, "s", &command))  
        return NULL;  
    sts = system(command);  
    return PyLong_FromLong(sts);  
}  

그 중 PyArgParseTuple (args, "s", & command) 에서 매개 변수 목록 에 오류 가 있 는 지 확인 하고 오 류 는 NULL 로 돌아 갑 니 다. 그렇지 않 으 면 명령 을 command 에 전달 합 니 다.
지금 은 우리 가 spam 을 실현 한 셈 이다.시스템 함수, 다음은 파 이 썬 이 그것 을 호출 할 수 있 도록 하 는 방법 에 대해 토론 할 것 입 니 다.
여기 서 우리 모듈 이름 은 spam 이 고 spam 입 니 다.시스템 은 그것 의 방법 이다.그래서 저희 가 스 팸 을...시스템 이 spam 방법 목록 에 추가 되 었 습 니 다.
static PyMethodDef SpamMethods[] = {  
    ...  
    {"system",  spam_system, METH_VARARGS,  
     "Execute a shell command."},  
    ...  
    {NULL, NULL, 0, NULL}        /* Sentinel */  
};  

그 중, METHVARARGS 는 파 이 썬 의 모듈 을 통 해 파 이 썬 해석 기와 C 함수 사이 에 파 이 프 를 전달 하 는 매개 변수 전달 의 표준 형식 입 니 다.METH 를 사용 하면KEYWORD 방식 은 Python 해석 기와 C 함수 간 에 Python 의 사전 형식 을 통 해 둘 사이 에 매개 변 수 를 전달 합 니 다.
시스템 은 함수 spam모듈 에 있 는 시스템 이름 입 니 다.
이제 방법 이 생 겼 습 니 다. 이 모듈 을 표징 하 는 구조 체 를 구축 한 다음 에 방법 목록 을 추가 해 야 합 니 다.다음 과 같다.
static struct PyModuleDef spammodule = {  
   PyModuleDef_HEAD_INIT,  
   "spam",   /* name of module */  
   spam_doc, /* module documentation, may be NULL */  
   -1,       /* size of per-interpreter state of the module, 
                or -1 if the module keeps state in global variables. */  
   SpamMethods  
};  

구조 체 를 정의 한 후 초기 화 함 수 를 정의 해 야 합 니 다.다음 과 같다.
PyMODINIT_FUNC PyInit_spam(void)  
{  
    return PyModule_Create(&spammodule);  
}  

메모: 초기 화 함수 에서 모듈 구조 체 에 꼭 들 어가 야 합 니 다.PyModule 로Create () 함수 가 모듈 구조 체 를 초기 화 합 니 다.그리고 Python 3 에서 초기 화 함 수 는 반드시 PyInitname () 방식 으로 이름 을 지 었 습 니 다.초기 화 함 수 는 static 성명 을 추가 하지 않 았 기 때문에 모듈 의 초기 화 함 수 는 모듈 의 유일한 대외 인터페이스 입 니 다.상술 한 재료 가 모두 준 비 된 후에 우 리 는 다음 에 어떻게 이 모듈 을 생 성 할 것 인 가 를 토론 할 것 이다.우 리 는 setup. py 파일 을 작성 하여 Extension 을 설명 하고 setuptools 도구 로 모듈 을 포장 합 니 다.코드 는 다음 과 같 습 니 다:
from setuptools import setup, Extension, distutils, Command, find_packages
C = Extension("spam",
              libraries=main_libraries,
              sources=['spammodule.c'],
              language='c',
              )

setup(name="spam", 
      version=1.0,
      ext_modules=[C],
      )

터미널 을 열 고 cd 를 setup. py 디 렉 터 리 에 입력 한 다음 두 명령 을 입력 하면 모듈 의 컴 파일 을 완성 할 수 있 습 니 다.그리고 import 명령 으로 spam 모듈 을 가 져 올 수 있 습 니 다.
python setup.py build python setup.py install  컴 파일 하 는 과정 에서 PyMODINITFUNC 방법 이 호출 되 어 spam 의 정 의 를 완 료 했 습 니 다.
요약: spammodule. c 파일 구축 - > \ # include 및 작성 방법 함수 - > 특정한 구조 체 구축 및 초기 화 함수 - > setup. py 파일 구축, 확장 및 포장 모듈 설명.
C + + Python 모듈 작성 --- 관련 설정
프로젝트 속성 설정:
1. release + x64 컴 파일 모드 를 선택 하 십시오. 그렇지 않 으 면 컴 파일 dll 이 잘못 되 었 습 니 다. (python 은 64 비트)
2. 다음 표 에서 말 한 항목 속성 을 설정 한 다음 에 '확인' 을 선택 합 니 다.
Tab
속성

상규
상규 > 대상 이름
지정  from...import  Python 에서 인용 한 모듈 의 이름 입 니 다. Python 모듈 을 정의 할 때 C + + 에서 같은 이름 을 사용 합 니 다. 항목 의 이름 을 모듈 이름 으로 사용 하려 면 기본 값 $(ProjectName) 를 유지 하 십시오.
 
상규 > 대상 확장자
.pyd
 
항목 기본 값 > 설정 형식
동적 라 이브 러 리 (. dll)
C/C++> 상규
추가 포함 디 렉 터 리
해당 설치 에 따라 Python "include" 폴 더 를 추가 합 니 다. 예 를 들 어  c:\Python36\include
C/C++> 프로세서
프로세서 정의
장차  Py_LIMITED_API;  문자열 (분점 포함) 의 시작 에 추가 합 니 다. 이 정 의 는 Python 에서 호출 할 수 있 는 일부 함 수 를 제한 하고 코드 를 Python 의 서로 다른 버 전에 서 쉽게 이식 할 수 있 도록 합 니 다.
C/C++> 코드 생 성
런 타임 라 이브 러 리
다 중 스 레 드 DLL (/ MD) (아래 "경고" 참조)
링크 기 > 상규
추가 라 이브 러 리 디 렉 터 리
해당 설치 에 따라. lib 파일 을 포함 하 는 Python "libs" 폴 더 를 추가 합 니 다. 예 를 들 어  c:\Python36\libs 。 (. lib 파일 을 포함 하 는 "libs" 폴 더 를 가리 키 며. py 파일 을 포함 하 는 Lib 폴 더 가 아 닙 니 다.)
 제시 하 다.
     프로젝트 속성 에서 C / C + + 옵션 카드 를 보지 못 하면 프로젝트 에 C / C + + 원본 파일 로 표 시 된 파일 이 포함 되 어 있 지 않 기 때 문 입 니 다. 만 든 원본 파일 에. c 나. cpp 확장자 가 없 으 면 이 럴 수 있 습 니 다. 예 를 들 어 '새 항목' 대화 상자 에 잘못 입력 하면  module.coo  module.cpp) 는 Visual Studio 에서 파일 을 만 들 지만 파일 형식 을 'C / C + 코드' 로 설정 하지 않 습 니 다. 바로 C / C + 속성 옵션 을 활성화 합 니 다.파일 이름 을 테이프 로 바 꿔 도  .cpp 이런 식별 오 류 는 여전히 존재 할 것 이다. 파일 형식 을 정확하게 설정 하기 위해 서 는 '솔 루 션 자원 관리자' 에서 파일 을 오른쪽 클릭 하고 '속성' 을 선택 한 다음 '파일 형식' 을 'C / C + 코드' 로 설정 하 십시오.
 경고 하 다.
     디 버 깅 설정 에 대해 서도 항상 'C / C + +' > '코드 생 성' > '런 타임 라 이브 러 리' 옵션 을 '다 중 스 레 드 DLL (/ MD)' 로 설정 합 니 다. 이 설정 은 디 버 깅 하지 않 은 Python 바 이 너 리 파일 을 생 성 할 때 사용 하 는 설정 이기 때 문 입 니 다. 공교롭게도 '다 중 스 레 드 디 버 깅 DLL (/ MDd)' 옵션 을 설정 하면 디 버 깅 설정 생 성 에 오류 가 발생 합 니 다. "C1189: Py LIMITED API 는 Py DEBUG, Py TRACE REFS, Py REF DEBUG 와 호 환 되 지 않 습 니 다." 삭제  Py_LIMITED_API  생 성 오 류 를 피하 기 위해 모듈 을 가 져 오 려 고 시도 할 때 Python 이 무 너 집 니 다. (다음 과 같이 붕 괴 는 DLL 쌍 에서 발생 합 니 다.  PyModule_Create  "심각 한 Python 오류: PyThreadState Get: 현재 스 레 드 없 음" 이라는 출력 메시지 가 호출 되 었 습 니 다.
/ MDd 옵션 은 Python 디 버 깅 바 이 너 리 파일 (예: python d. exe) 을 만 드 는 데 사 용 됩 니 다. 그러나 확장 DLL 에서 이 옵션 을 선택 하면  Py_LIMITED_API 생 성 오류.
 
테스트 코드:
#include  

//C++  
int Add(int x, int y)
{
    return x + y;
}

int Del(int x, int y)
{
    return x - y;
}


//***********************************************************    ,       Python   
static PyObject* WrappAdd(PyObject* self, PyObject* args)
{
    int x, y;
    if (!PyArg_ParseTuple(args, "ii", &x, &y))
    {
        return NULL;
    }
    return Py_BuildValue("i", Add(x, y));
}

static PyObject* WrappDel(PyObject* self, PyObject* args)
{
    int x, y;
    if (!PyArg_ParseTuple(args, "ii", &x, &y))
    {
        return NULL;
    }
    return Py_BuildValue("i", Del(x, y));
}


//*************************************************************     ,  Python    C++    、     
static PyMethodDef py_module_methods[] = {
        { "Add", WrappAdd, METH_VARARGS, "Execute a shell command." },
        { "Del", WrappDel, METH_VARARGS, "Execute a shell command." },
        { NULL, NULL, 0, NULL } /* Sentinel */
};


//**************************************************************        

//--- #define PyMODINIT_FUNC extern "C" __declspec(dllexport) void
// PyMODINIT_FUNC     ,    extern "C"
//extern "C"

PyMODINIT_FUNC initpy_module(void)
{    
    
    
    PyObject *m;
    m = Py_InitModule("py_module", py_module_methods);
    if (m == NULL)
        return;
    PyImport_AddModule("py_module");

}

/*

           init+   。       non-static 。
       py_module.pyd,             initpy_module,
 Py_InitModule()           “py_module”,  Python       ;

*/

2. PyTorch 의 C 확장
_C 모듈 의 구축
PyTorch 에 서 는 여러 종류 가 계승 합 니 다C 모듈 의 내용 입 니 다.C 모듈 에는 Tensor, Storage 등 상용 유형의 C 언어 구현 이 포함 됐다.Tensor 타 입 은 로 볼 수 있 습 니 다.C 모듈 의 유형 대상 은 포함 되 어 있 는 관계 입 니 다.
우선, 우리 먼저 보 자C 모듈 의 실현.이 과정 은 첫 번 째 부분 에서 말 한 것 과 매우 유사 하 다.C 모듈 의 주체 성명 정의 코드 는 torch / csrc / Module. cpp 에 있 습 니 다.이 파일 의 한 줄 도 \ # include 입 니 다. 함수 의 방법 은 여러 가지 가 있 습 니 다. 여 기 는 토론 하지 않 겠 습 니 다.다음 에 우 리 는 모듈 구조 체 의 정의 와 함수 초기 화 성명 을 찾 았 다.다음 과 같다.
#if PY_MAJOR_VERSION == 2
PyMODINIT_FUNC init_C()
#else
PyMODINIT_FUNC PyInit__C()
#endif
{
    ...
#if PY_MAJOR_VERSION == 2
    ASSERT_TRUE(module = Py_InitModule("torch._C", methods.data()));
#else
    static struct PyModuleDef torchmodule = {
        PyModuleDef_HEAD_INIT,
        "torch._C",
        NULL,
        -1,
        methods.data()
    };
    ASSERT_TRUE(module = PyModule_Create(&torchmodule));
#endif
    ...
    ASSERT_TRUE(THPDoubleTensor_init(module));
    ASSERT_TRUE(THPFloatTensor_init(module));
    ASSERT_TRUE(THPHalfTensor_init(module));
    ASSERT_TRUE(THPLongTensor_init(module));
    ASSERT_TRUE(THPIntTensor_init(module));
    ASSERT_TRUE(THPShortTensor_init(module));
    ASSERT_TRUE(THPCharTensor_init(module));
    ASSERT_TRUE(THPByteTensor_init(module));
    ...
}    

메모: 버 전의 Python 에 따라 함수 의 이름 을 초기 화 하 는 방식 이 다 릅 니 다.원본 코드 에서 \ # if, endif 방식 으로 구분 합 니 다.이해 하기 편 하도록 파 이 썬 3 을 예 로 들 자.코드 정 리 는 다음 과 같 습 니 다.
PyMODINIT_FUNC PyInit__C()
{
    ...
    static struct PyModuleDef torchmodule = {
        PyModuleDef_HEAD_INIT,
        "torch._C",
        NULL,
        -1,
        methods.data()
    };
    ASSERT_TRUE(module = PyModule_Create(&torchmodule));
    ...
    ASSERT_TRUE(THPDoubleTensor_init(module));
    ASSERT_TRUE(THPFloatTensor_init(module));
    ASSERT_TRUE(THPHalfTensor_init(module));
    ASSERT_TRUE(THPLongTensor_init(module));
    ASSERT_TRUE(THPIntTensor_init(module));
    ASSERT_TRUE(THPShortTensor_init(module));
    ASSERT_TRUE(THPCharTensor_init(module));
    ASSERT_TRUE(THPByteTensor_init(module));
    ...
}    

우 리 는 이곳 의 성명 과 정의 방식 이 이전 과 다르다 는 것 을 볼 수 있다.구조 체 의 정 의 는 초기 화 함수 에 놓 여 있 습 니 다. 모듈 이름 은 torch. 입 니 다.C。이어서 우 리 는 setup. py 파일 을 열 었 다.
...
main_sources = [
    "torch/csrc/PtrWrapper.cpp",
    "torch/csrc/Module.cpp",
    "torch/csrc/Generator.cpp",
    "torch/csrc/Size.cpp",
    "torch/csrc/Exceptions.cpp",
    "torch/csrc/Storage.cpp",
    "torch/csrc/DynamicTypes.cpp",
    "torch/csrc/byte_order.cpp",
    "torch/csrc/utils.cpp",
    "torch/csrc/expand_utils.cpp",
    "torch/csrc/utils/invalid_arguments.cpp",
    ...
]
main_sources += split_types("torch/csrc/Tensor.cpp")
...
C = Extension("torch._C",
              libraries=main_libraries,
              sources=main_sources,
              language='c++',
              extra_compile_args=main_compile_args + extra_compile_args,
              include_dirs=include_dirs,
              library_dirs=library_dirs,
              extra_link_args=extra_link_args + main_link_args + [make_relative_rpath('lib')],
              )
extensions.append(C)
...
setup(name="torch", version=version,
      description="Tensors and Dynamic neural networks in Python with strong GPU acceleration",
      ext_modules=extensions,
      cmdclass=cmdclass,
      packages=packages,
      package_data={'torch': [
          'lib/*.so*', 'lib/*.dylib*',
          'lib/torch_shm_manager',
          'lib/*.h',
          'lib/include/TH/*.h', 'lib/include/TH/generic/*.h',
          'lib/include/THC/*.h', 'lib/include/THC/generic/*.h',
          'lib/include/ATen/*.h',
      ]},
      install_requires=['pyyaml', 'numpy'],
      )

지난 절 과 같은 과정 을 볼 수 있 으 니 더 이상 군말 하지 않 겠 다.컴 파일 하 는 과정 에서Module. cpp 의 PyMODINITFUNC 함수 호출, torch 완료.C 의 정의 와 다양한 종류의 Tensor 초기 화 함수 호출.ASSERTTRUE(THPDoubleTensor_init(module))。
주의: 상기 코드 중의 mainsources 에는 Tensor. cpp 라 는 파일 도 포함 되 어 있 습 니 다.그것 은 주로 여러 종류의 Tensor 의 실현 을 포함한다.

좋은 웹페이지 즐겨찾기