2. 확장모듈
• 파이썬에서 C/C++라이브러리 함수 또는 시스템 콜을 할 수 있는 새로운 내장객체 타입 구현
• 하드웨어를 제어하는 경우 C기반이 많고 빠르다.
• 처리 속도는 연산량이 많아지면, 파이썬에 비해서 C가 빠르다.
• 반드시, C/C++ 에서 #include “python.h” 필요.
3. 간단한 예제
strlen() 함수를 파이썬에서 써보자.
1. C 모듈 만들기
- spammodule.c (모듈이름 + module.c) 작성
주의사항
- 헤더파일 include 시, python.h 를 제일 먼저 포함시켜야 함.
- python.h 내부에는 기본적으로 <stdio.h><string.h><error.h><limits.h><stdlib.h> 포함.
4. 간단한 예제
2. 모듈 초기화
- 파이썬 내에서 1) 모듈을 찾고, 2) 초기화를 하고, 3) 지역 이름공간에 이름을 정의 하기
때문에, C/C++ 모듈에서 2단계와 3단계 부분이 구현이 되어야 함.
- Py_InitModule() : 모듈 초기화에 사용
- 확장모듈 호출시, PyInit_spam() 함수 실행 (형식 준수 PyInit_<module_name>)
- 파이썬 인터프리터에서 import시, 맨 처음 PyInit_<module_name>()를 찾아서 실행하는 구조
- PyModule_Create()
: 생성할 모듈 생성정보를 담고 있는 구조체를 가지고 모듈 생성
: 생성후 해당 모듈 객체의 포인터를 반환하고 에러 발생시, NULL반환
: 이것을 가지고 sys.module 에 모듈을 등록한다.
5. 간단한 예제
3. 모듈 빌드
윈도우 빌드
: 비주얼 스튜디오 이용해서 Dll생성
설정(프로젝트 속성창)
- C/C++의 일반탭에서 추가포함 디렉토리에서 Python.h가 있는 디렉토리 지정
- 링커 -> 일반메뉴에서 추가라이브러리 디렉토리에서 python30.lib가 있는 디렉토리 지정
- 링커 -> 일반 -> 입력
- 명령줄 메뉴 하단의 추가옵션에 /expo추가종속성 python30.lib rt:PyInit_spam 입력
- 링커 -> 일반 출력파일의 확장자를 pyd로 변경
주의사항
- 빌드 옵션에서 릴리즈 모드로 빌드
- 파이썬 설치시 기본제공되는 lib는 디버그 용이 아닌 릴리즈용.
- 비주얼스튜디오 설정은 파이썬 설치 버전에 따라 변경되오니, 잘 살필것.
6. 간단한 예제
3. 모듈 빌드
리눅스 빌드
: 입력기를 통해서 spammodule.c 작성후, distutils를 이용해서 간단히 빌드
: 189p
7.
8. 파이썬/C API
파이썬/C API : 파이썬 자료형을 C언어에서 사용하게 하겠다.
PyArg_ParseTuple() 함수
int PyArg_ParseTuple(PyObject *arg, char *format...);
1) 파이썬에서 전달된 변수
2) PyObject를 어떤 형으로 변환할것인지 지정
3) 변환한 값을 받을 변수 지정
- i, s, f, l, y, u, O, S 등 다양한 형 지정가능(190p)
- s#과 같이 #을 붙이면 문자열과 문자열 길이를 자동 변환
- 여러개의 기호를 묶어서 포맷으로 설정.
- PyObject에 대한 타입 검사 함수들 존재(191p)
9. 파이썬/C API
Py_BuildValue() 함수
PyObject *Py_BuildValue(char * format…);
- C의 자료형을 파이썬의 자료형으로 변환
- 단, 포인터형은 변환할수 없음.
- Py_BuildValue()비어 있으면, 자동으로 None 을 만든다.
10. 에러처리
파이썬/C API 함수들은 이미 내부적으로 예외처리 되어 있음.
사용자 정의의 C/C++ 함수에서 에러상태 설정과 에러값(NULL) 리턴
Void PyErr_SetString(PyObject *type, const char *message);
1) 예외 타입
2) 에러에 대한 메시지
PyErr_SetFromErrno()
문자열 대신 C전역변수인 errno값을 반환
11. 참조카운트
• C/C++ 에서의 동적 메모리 할당
- C : malloc, free
- C++ : new, delete
파이썬 메모리 관리 방식 = 가비지컬렉션
- 객체의 레퍼런스가 필요할때마다 새로운 공간 확보 하지 않음.
- 타입에 대한 인스턴스 레퍼런스 카운트를 통한 객체 참조 전략.
- 모든 객체는 자신이 참조된 횟수를 관리하는 카운터를 가지고 있고, 객체가 참조될 때마다
카운터를 하나씩 증가시키고, 참조가 해제 될 때 마다 레퍼런스 카운터를 하나 감소시킨다.
- 만약 카운터가 0이 되면 그때서야 객체는 메모리 공간에서 삭제된다.
파이썬 – C/C++ 연동이기 때문에, C/C++ 에서 개발자가 객체 참조 카운트 증분에 대해서 구현
12. 참조카운트
• 참조카운트를 위한 2개의 매크로 함수
- Py_INCREF() : 참조 카운트 증가
- Py_DECREF() : 참조 카운트 감소 및 메모리 해제
언제 사용해야 하는가?
파이썬에서는 어떤 것도 객체를 소유할 수 없고,
단지 객체의 참조(reference)만을 가질 수 있다
- 레퍼런스를 사용하고 있는 곳에서는 레퍼런스를 생성하고,
- 더 이상 필요 없을 경우 Py_DECREF( )를 호출해줘야 메모리 누수가 일어나지 않는다.
>>> setList = [ ] #사전 객체(setList)의 레퍼런스 생성
>>> borrow_ref = setList #borrow_ref는 setList와 똑 같은 레퍼런스를 가리키고 있음
- 빌려온 경우, 증가 감소 해주면 안된다.
13. 참조카운트
레퍼런스 소유권 법칙
• 파이썬 C API 함수
- 함수마다 레퍼런스 전달 방식이 다르다.
- 레퍼런스의 소유권한을 넘겨주거나, 빌려주거나.
레퍼런스 소유권 이전 함수
- PyLong_FromLong()/ Py_BuildValue()
레퍼런스 소유권 빌려주는 함수
- PyTuple_GetItem( ), PyList_GetItem( ), PyDict_GetItem( ), PyDict_GetItemString()
- 튜플, 리스트, 사전과 관련된 함수들은 빌려온 레퍼런스를 넘겨준다.
파이썬 매뉴얼을 볼 수 밖에 없다.
14. 참조카운트
레퍼런스 소유권 법칙
PyObject* Py_BuildValue(const char *format, ...)¶
Return value: New reference.
Create a new value based on a format string similar to those accepted by the PyArg_Parse*() family of functions and a
sequence of values. Returns the value or NULL in the case of an error; an exception will be raised if NULL is returned.
- Py_BuildValue 는 매뉴얼을 보면 반환값을 새로운 레퍼런스를생성해서 내보내고 있음.
15. 참조카운트
레퍼런스 소유권 법칙
함수 매개변수로 이용 시, 빌려온 레퍼런스로 전달된다.
tuple = PyTuple_GetItem(list, 1); //tuple-빌려온 레퍼런스, Py_INCREF 호출할 필요없음
Py_INCREF(list) // 참조카운트를 증가시켜lifetime 보장시켜줌
long l = 0;
PyObject* item = PyLong_FromLong((long)l); //item은 new reference
…
Py_DECREF(item) //따라서, => 참조카운터감소!!!
Py_DECREF(list) // list의참조카운터감소
- 함수로 전달된 인자는 모두 ‘빌려온’ 레퍼런스, 따라서 Py_DECREF(list) 시키면 안된다.
- 한 곳에서 함수가 정상적으로 수행될 때까지 빌려온 레퍼런스의 lifetime을 보장해줘야
한다. 그러기 위해서는 Py_INCREF( )를 호출해 참조 카운트를 하나 증가시켜서 사용해야
한다.
- 호출한 쪽에서 소유권을 넘겨 받았으면 반드시 Py_DECREF( )를 호출해주고, 빌려온
레퍼런스라도 호출한 쪽에서 소유권을 유지하고 싶으면 Py_INCREF( )를 사용해야 한다.
16. 참조카운트
레퍼런스 소유권 법칙
예외
• PyTuple_SetItem( )와 PyList_SetItem( )함수는 소유권한을 빼앗아 갑니다. (함수 호출이
실패 해도 빼앗아 갑니다)
Note : This function “steals” a reference to item and discards a reference
to an item already in the list at the affected position.
list = PyList_GetItem(list, 1);
long l = 0;
PyObject* item = PyLong_FromLong((long)l);
PyList_SetItem(list, l, item);
#Py_DECREF(item)을 하면 안됩니다. item의 소유권을 뺏겻어요!
17. 참조카운트
레퍼런스 소유권 법칙
예외
빌려온 레퍼런스라도 Py_INCREF( )를 반드시 해줘야 하는 경우
- 호출한 쪽에서 소유권을 유지하고 싶을 경우
void bug(PyObject* list)
{
PyObject* item = PyList_GetItem(list, 0);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0);
}
- Item 에 list[0]을 저장. 그리고 SetItem 이용 list[1] =0 대입, PyObject_Print(item, stdout, 0); 에서
오류 발생 가능성 있음.
- Setitem 을 하는 과정에서 list 내 모든 아이템에 대한 레퍼런스를 재배치 하는 과정이 있을수가
있다. 즉, 모든 레퍼런스를 삭제하고, 다시생성.
- 레퍼런스 카운트를 증가시켜서 item 이 가지고 있는 레퍼런스가 삭제되지 않도록 해야함.
18. 참조카운트
레퍼런스 소유권 법칙
NULL 검사
PyObject *item = NULL;
...
Py_DECREF(item);
- NULL 인 경우, Py_DECREF() 를 호출하면 어떻게 되나?
• 파이썬에서는 해당 레퍼런스가 NULL인지를 체크하는 매크로 함수가 없음.
• Py_XINCREF( )와 Py_XDECREF( )는 내부에 NULL혹은 None인지를 검사하고 NULL일 경우 참조
카운트에 대한 작업을 수행하지 않는다.
PyObject *item = NULL;
...
Py_XDECREF(item);
19. 확장 타입
• C를 이용해서 모듈 뿐만 아니라, 파이썬의 리스트 같은 타입 작성 가능.
• 파이썬에서 쓸수 있는 새로운 자료형을 C에서 만들어서 쓸수 있다.
• 타입 정의
• 타입내 인스턴스 변수 정의
• 생성자, 소멸자 정의
• 사용자 함수 정의
• 198p
20. ctypes
외부 라이브러리 호출을 위한 완전 간단한 방법이 있다.
파이썬에서 제공하는 ctypes 모듈을 사용.
- 파이썬의 외부 함수 인터페이스(FFI) 라이브러리로, 파이썬 2.5부터 기본으로 포함되어 있다. 윈도의 DLL과 같은 동적
라이브러리에 있는 함수를 직접 호출할 수 있으며, 다양한 C 자료형을 다루기 위한 인터페이스를 제공한다. 이를 사용해 순수
파이썬 코드만으로 확장 모듈을 구현할 수도 있다.
>>> from ctypes import *
>>> printf = cdll.msvcrt.printf
>>> printf("hello worldn")
hello world 12
>>> printf.restype = None
>>> printf("hello worldn")
hello world
- 윈도우 환경의 printf 함수를 가져와서 사용하는 예제
- C의 데이터 타입을 바로 쓸 수 가 있다. (213P)
- 윈도우의 메시지 박스도 ctypes를 통해서 랩핑해서 사용할 수 있다.
http://docs.python.org/library/ctypes.html
21. ctypes
ctypes type C type Python type
c_bool _Bool bool (1)
c_char char 1-character string
c_wchar wchar_t 1-character unicode string
c_byte char int/long
c_ubyte unsigned char int/long
c_short short int/long
c_ushort unsigned short int/long
c_int int int/long
c_uint unsigned int int/long
c_long long int/long
c_ulong unsigned long int/long
c_longlong __int64 or long long int/long
unsigned __int64 or unsigned long lo
c_ulonglong int/long
ng
c_float float float
c_double double float
c_longdouble long double float
c_char_p char * (NUL terminated) string or None
c_wchar_p wchar_t * (NUL terminated) unicode or None
c_void_p void * int/long or None
22. ctypes
• 사용자 정의 라이브러리 연동하기
- 윈도우/리눅스에서 모듈 생성
윈도우
• C:Windowssystem32 폴더로 test.dll 파일을 복사합니다.
• >>> from ctypes import *
>>> libtest = cdll.LoadLibrary('test.dll')
>>> print libtest.multiply(2, 2)
23. ctypes
• 사용자 정의 라이브러리 연동하기
- 윈도우/리눅스에서 모듈 생성
리눅스
• dynamic library로 만듭니다.
gcc -shared libtest.o -o libtest.so
• 터미널에서 python을 실행하고 libtest.so를 사용합니다.
• >>> from ctypes import *
>>> import os
>>> libtest = cdll.LoadLibrary(os.getcwd() + '/libtest.so')
>>> print libtest.multiply(2, 2)
[출처] : http://blog.naver.com/timberx?Redirect=Log&logNo=30109263914