-
Python 모듈 C언어로 만들기컴퓨터/파이썬 2020. 10. 22. 00:34728x90반응형
Python 모듈
1. 소개
전 ctype으로 C언어 코딩 실행하기와 비슷한데
실제로 pip으로 설치할 수 있는 파이썬 패키지를 C언어로 만들어 볼 것이다.
Python Github의 C 파일들은 보면 문법이 어느 정도 감이 잡힐 것이다.
할 것
팩토리얼(N!) 결과를 구해내는 방법을
C언어 모듈, Cython (.pyx) 모듈, 기본 파이썬 함수
위 3가지를 비교할 것이다.
C언어 파이썬 모듈 제작
1. 우선 setup.py을 대충 아래처럼 만든다.
try: from setuptools import setup, Extension except ImportError: from distutils.core import setup, Extension setup( name="C Factorial", version="0.1", description="C", author="", author_email="", license="MIT", ext_modules=[Extension("pycmath", ["src/factorial_mod.c"])], python_requires=">=2.8", include_package_data=True, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], zip_safe=False, )
2. C 코드 작성
Python.h 파일은 파이썬/include 폴더나, anaconda는 아나콘다 설치 폴더\include 폴더에 있다.
그리고, vscode를 이용 중이라면 includePath에 위 폴더를 추가하면 된다.
{ "configurations": [ { "name": "Win32", "includePath": ["${workspaceFolder}/src", "${workspaceFolder}/include"], "defines": ["_DEBUG", "UNICODE", "_UNICODE"], ~~ } ], }
3. 팩토리얼 함수
매우 큰 크기의 수를 return 해야 하므로 long long을 타입으로 지정했다.
static long long c_fact(long N); static long long c_fact(long N) { long long sum = 1; int i; for (i = 1; i <= N; i++) { sum *= i; } return sum; }
4. 인터페이스
C 코드를 사용하게 해주는 파이썬 인터페이스를 아래처럼 만든다.
(Python으로 C를 작성할 때 거의 모든 것을 PyObject *Name으로 작성하면 된다.)
static PyObject *python_factorial(PyObject *module, PyObject *arg) { PyObject *ret = NULL; // 결과 assert(arg); Py_INCREF(arg); if (!PyLong_CheckExact(arg)) { PyErr_SetString(PyExc_ValueError, "Argument is not an integer."); goto except; } long ordinal = PyLong_AsLong(arg); long long result = c_fact(ordinal); // 팩토리얼 계산 ret = PyLong_FromLongLong(result); // 파이썬 long long assert(!PyErr_Occurred()); assert(ret); goto finally; except: Py_XDECREF(ret); ret = NULL; finally: Py_DECREF(arg); return ret; }
5. 파이썬 모듈 제작 함수
METH_O는 PyArg_ParseTuple()을 부르는 대신 PyObject* 타입으로 지정
PyMODINIT_FUNC PyInit_모듈이름(void), 모듈 이름으로 안 하면 오류가 나는 것 같다.
static PyMethodDef pycmathExt_methods[] = { {"factorial", python_factorial, METH_O, "Factorial of N."}, {NULL, NULL, 0, NULL} /* {함수 이름, 파이썬 인터페이스, 매개변수 타입, "설명"} */ }; #if PY_MAJOR_VERSION >= 3 PyDoc_STRVAR(module_doc, "Factorial in C."); static struct PyModuleDef pycmathExt = { PyModuleDef_HEAD_INIT, "pycmath", // 모듈 이름 module_doc, -1, pycmathExt_methods, NULL, NULL, NULL, NULL}; PyMODINIT_FUNC PyInit_pycmath(void) { return PyModule_Create(&pycmathExt); } #endif
더보기#include <Python.h> static long long c_fact(long N); static long long c_fact(long N) { long long sum = 1; int i; for (i = 1; i <= N; i++) { sum *= i; } return sum; } static PyObject *python_factorial(PyObject *module, PyObject *arg) { PyObject *ret = NULL; assert(arg); Py_INCREF(arg); if (!PyLong_CheckExact(arg)) { PyErr_SetString(PyExc_ValueError, "Argument is not an integer."); goto except; } long ordinal = PyLong_AsLong(arg); long long result = c_fact(ordinal); ret = PyLong_FromLongLong(result); assert(!PyErr_Occurred()); assert(ret); goto finally; except: Py_XDECREF(ret); ret = NULL; finally: Py_DECREF(arg); return ret; } static PyMethodDef pycmathExt_methods[] = { {"factorial", python_factorial, METH_O, "Factorial of N."}, {NULL, NULL, 0, NULL} /* sentinel */ }; #if PY_MAJOR_VERSION >= 3 PyDoc_STRVAR(module_doc, "Factorial in C."); static struct PyModuleDef pycmathExt = { PyModuleDef_HEAD_INIT, "pycmath", module_doc, -1, pycmathExt_methods, NULL, NULL, NULL, NULL}; PyMODINIT_FUNC PyInit_pycmath(void) { return PyModule_Create(&pycmathExt); } #endif
빌드 및 설치
python setup.py build_ext --inplace python setup.py install
사용법
import pycmath pycmath.factorial(100003)
Cython으로 모듈 제작
factorial.pyx 형식으로 만든 후, cdef나 cpdef 이용
cdef: c언어에서 불릴 함수, 무조건 다른 함수에서 실행되는 방법이여야 함.
def, cpdef: 파이썬에서 불릴 함수 (ex. fact_cpdef(1000)으로 바로 파이썬에서 이용)
1. cdef 이용
def factorial(long n): return fact_in_c(n) cdef long long fact_in_c(long n): sum = 1 for i in range(1, n + 1): sum *= i return sum
2. cpdef 이용
cpdef long long fact_cpdef(long n): sum = 1 for i in range(1, n + 1): sum *= i return sum
3. setup.py
cythonize을 이용하면 된다.
try: from setuptools import setup except ImportError: from distutils.core import setup from Cython.Build import cythonize setup( name="C Factorial", version="0.1", description="C", author="", author_email="", license="MIT", ext_modules=cythonize("src/factorial.pyx", language_level="3"), python_requires=">=2.8", include_package_data=True, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], zip_safe=False, )
결과
memory-profiler를 설치해서 메모리 사용량(메가바이트) 까지 확인해보았다.
pip install memory-profiler
from factorial import factorial, factorial_cdef from timeit import default_timer, timeit from memory_profiler import memory_usage import pycmath # 파이썬 순수 코드 def pyfactorial(n): sum = 1 for i in range(1, n + 1): sum *= i return sum mem_usage1 = memory_usage() start = default_timer() pycmath.factorial(100003) # C언어 버전 실행 print(f"C: {default_timer() - start:.5f}초") mem_usage2 = memory_usage()[0] - mem_usage1[0] print(mem_usage2, "Mb") mem_usage1 = memory_usage() start = default_timer() factorial(100003) # Cython cpdef 이용 print(f"Cython: {default_timer() - start:.5f}초") mem_usage2 = memory_usage()[0] - mem_usage1[0] print(mem_usage2, "Mb") mem_usage1 = memory_usage() start = default_timer() factorial_cdef(100003) # Cython cdef 이용 print(f"Cython: {default_timer() - start:.5f}초") mem_usage2 = memory_usage()[0] - mem_usage1[0] print(mem_usage2, "Mb") mem_usage1 = memory_usage() start = default_timer() pyfactorial(100003) # Python 함수 이용 print(f"Python: {default_timer() - start:.5f}초") mem_usage2 = memory_usage()[0] - mem_usage1[0] print(mem_usage2, "Mb")
# 결과 C: 0.00003초 0.0078125 Mb Cython (cpdef): 0.00450초 0.0078125 Mb Cython (cdef): 0.00278초 0.0 Mb Python: 0.00593초 0.0 Mb
결론 : C언어로 작성하고 python에 이용하자.
외전
내 모듈을 pypi에 업로드 하고 싶으면
pypi 사이트 로그인 후, pypi.org/
아래 명령어 입력
python setup.py sdist bdist_wheel twine upload --skip-existing dist/*
참고
파이썬 C모듈 제작 기본 문법 Github
파이썬 공식 C API 설명
728x90'컴퓨터 > 파이썬' 카테고리의 다른 글
Cython을 이용한 Bubble Sort (0) 2020.10.24 Python에서 C/C++언어 함수 실행하기 (0) 2020.10.21 Python kth Hamming number (해밍 수) (0) 2020.10.19