23
파파파 파파파 9 파 C/C++ 파파 Ahn Seong Hyun

파이썬 스터디 9장

Embed Size (px)

Citation preview

Page 1: 파이썬 스터디 9장

파이썬 스터디 9 장C/C++ 연동

Ahn Seong Hyun

Page 2: 파이썬 스터디 9장

확장모듈

• 파이썬에서 C/C++ 라이브러리 함수 또는 시스템 콜을 할 수 있는 새로운 내장객체 타입 구현

• 하드웨어를 제어하는 경우 C 기반이 많고 빠르다 .

• 처리 속도는 연산량이 많아지면 , 파이썬에 비해서 C 가 빠르다 .

• 반드시 , C/C++ 에서 #include “python.h” 필요 .

Page 3: 파이썬 스터디 9장

간단한 예제

strlen() 함수를 파이썬에서 써보자 .

1. C 모듈 만들기

- spammodule.c ( 모듈이름 + module.c) 작성

주의사항

- 헤더파일 include 시 , python.h 를 제일 먼저 포함시켜야 함 . - python.h 내부에는 기본적으로 <stdio.h><string.h><error.h><limits.h><stdlib.h> 포함 .

Page 4: 파이썬 스터디 9장

간단한 예제

2. 모듈 초기화

- 파이썬 내에서 1) 모듈을 찾고 , 2) 초기화를 하고 , 3) 지역 이름공간에 이름을 정의 하기 때문에 , C/C++ 모듈에서 2 단계와 3 단계 부분이 구현이 되어야 함 .

- Py_InitModule() : 모듈 초기화에 사용

- 확장모듈 호출시 , PyInit_spam() 함수 실행 ( 형식 준수 PyInit_<module_name>)- 파이썬 인터프리터에서 import 시 , 맨 처음 PyInit_<module_name>() 를 찾아서 실행하는 구조

- PyModule_Create() : 생성할 모듈 생성정보를 담고 있는 구조체를 가지고 모듈 생성

: 생성후 해당 모듈 객체의 포인터를 반환하고 에러 발생시 , NULL 반환 : 이것을 가지고 sys.module 에 모듈을 등록한다 .

Page 5: 파이썬 스터디 9장

간단한 예제

3. 모듈 빌드

윈도우 빌드: 비주얼 스튜디오 이용해서 Dll 생성

설정 ( 프로젝트 속성창 )- C/C++ 의 일반탭에서 추가포함 디렉토리에서 Python.h 가 있는 디렉토리 지정- 링커 -> 일반메뉴에서 추가라이브러리 디렉토리에서 python30.lib 가 있는 디렉토리 지정- 링커 -> 일반 -> 입력- 명령줄 메뉴 하단의 추가옵션에 /expo 추가종속성 python30.lib rt:PyInit_spam 입력 - 링커 -> 일반 출력파일의 확장자를 pyd 로 변경

주의사항

- 빌드 옵션에서 릴리즈 모드로 빌드- 파이썬 설치시 기본제공되는 lib 는 디버그 용이 아닌 릴리즈용 . - 비주얼스튜디오 설정은 파이썬 설치 버전에 따라 변경되오니 , 잘 살필것 .

Page 6: 파이썬 스터디 9장

간단한 예제

3. 모듈 빌드

리눅스 빌드: 입력기를 통해서 spammodule.c 작성후 , distutils 를 이용해서 간단히 빌드

: 189p

Page 7: 파이썬 스터디 9장
Page 8: 파이썬 스터디 9장

파이썬 /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)

Page 9: 파이썬 스터디 9장

파이썬 /C API

Py_BuildValue() 함수

PyObject *Py_BuildValue(char * format…);

- C 의 자료형을 파이썬의 자료형으로 변환- 단 , 포인터형은 변환할수 없음 . - Py_BuildValue() 비어 있으면 , 자동으로 None 을 만든다 .

Page 10: 파이썬 스터디 9장

에러처리 파이썬 /C API 함수들은 이미 내부적으로 예외처리 되어 있음 .

사용자 정의의 C/C++ 함수에서 에러상태 설정과 에러값 (NULL) 리턴

Void PyErr_SetString(PyObject *type, const char *message);

1) 예외 타입2) 에러에 대한 메시지

PyErr_SetFromErrno()

문자열 대신 C 전역변수인 errno 값을 반환

Page 11: 파이썬 스터디 9장

참조카운트• C/C++ 에서의 동적 메모리 할당- C : malloc, free- C++ : new, delete

파이썬 메모리 관리 방식 = 가비지컬렉션

- 객체의 레퍼런스가 필요할때마다 새로운 공간 확보 하지 않음 .

- 타입에 대한 인스턴스 레퍼런스 카운트를 통한 객체 참조 전략 .

- 모든 객체는 자신이 참조된 횟수를 관리하는 카운터를 가지고 있고 , 객체가 참조될 때마다 카운터를 하나씩 증가시키고 , 참조가 해제 될 때 마다 레퍼런스 카운터를 하나 감소시킨다 .

- 만약 카운터가 0 이 되면 그때서야 객체는 메모리 공간에서 삭제된다 .

파이썬 – C/C++ 연동이기 때문에 , C/C++ 에서 개발자가 객체 참조 카운트 증분에 대해서 구현

Page 12: 파이썬 스터디 9장

참조카운트• 참조카운트를 위한 2 개의 매크로 함수 - Py_INCREF() : 참조 카운트 증가 - Py_DECREF() : 참조 카운트 감소 및 메모리 해제

언제 사용해야 하는가 ?

파이썬에서는 어떤 것도 객체를 소유할 수 없고 , 단지 객체의 참조 (reference) 만을 가질 수 있다

- 레퍼런스를 사용하고 있는 곳에서는 레퍼런스를 생성하고 , - 더 이상 필요 없을 경우 Py_DECREF( ) 를 호출해줘야 메모리 누수가 일어나지 않는다 .

- 빌려온 경우 , 증가 감소 해주면 안된다 .

>>> setList = [ ] # 사전 객체 (setList) 의 레퍼런스 생성>>> borrow_ref = setList #borrow_ref 는 setList 와 똑 같은 레퍼런스를 가리키고 있음

Page 13: 파이썬 스터디 9장

참조카운트 레퍼런스 소유권 법칙

• 파이썬 C API 함수- 함수마다 레퍼런스 전달 방식이 다르다 . - 레퍼런스의 소유권한을 넘겨주거나 , 빌려주거나 .

레퍼런스 소유권 이전 함수 - PyLong_FromLong()/ Py_BuildValue()

레퍼런스 소유권 빌려주는 함수 - PyTuple_GetItem( ), PyList_GetItem( ), PyDict_GetItem( ), PyDict_GetItemString()- 튜플 , 리스트 , 사전과 관련된 함수들은 빌려온 레퍼런스를 넘겨준다 .

파이썬 매뉴얼을 볼 수 밖에 없다 .

Page 14: 파이썬 스터디 9장

참조카운트 레퍼런스 소유권 법칙

- Py_BuildValue 는 매뉴얼을 보면 반환값을 새로운 레퍼런스를생성해서 내보내고 있음 .

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.

Page 15: 파이썬 스터디 9장

참조카운트 레퍼런스 소유권 법칙 함수 매개변수로 이용 시 , 빌려온 레퍼런스로 전달된다 .

- 함수로 전달된 인자는 모두 ‘빌려온’ 레퍼런스 , 따라서 Py_DECREF(list) 시키면 안된다 .

- 한 곳에서 함수가 정상적으로 수행될 때까지 빌려온 레퍼런스의 lifetime 을 보장해줘야 한다 . 그러기 위해서는 Py_INCREF( ) 를 호출해 참조 카운트를 하나 증가시켜서 사용해야 한다 .

- 호출한 쪽에서 소유권을 넘겨 받았으면 반드시 Py_DECREF( ) 를 호출해주고 , 빌려온 레퍼런스라도 호출한 쪽에서 소유권을 유지하고 싶으면 Py_INCREF( ) 를 사용해야 한다 .

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 의참조카운터감소

Page 16: 파이썬 스터디 9장

참조카운트 레퍼런스 소유권 법칙

예외 • 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 의 소유권을 뺏겻어요 !

Page 17: 파이썬 스터디 9장

참조카운트 레퍼런스 소유권 법칙

예외 빌려온 레퍼런스라도 Py_INCREF( ) 를 반드시 해줘야 하는 경우 - 호출한 쪽에서 소유권을 유지하고 싶을 경우

- Item 에 list[0] 을 저장 . 그리고 SetItem 이용 list[1] =0 대입 , PyObject_Print(item, stdout, 0); 에서 오류 발생 가능성 있음 .

- Setitem 을 하는 과정에서 list 내 모든 아이템에 대한 레퍼런스를 재배치 하는 과정이 있을수가 있다 . 즉 , 모든 레퍼런스를 삭제하고 , 다시생성 .

- 레퍼런스 카운트를 증가시켜서 item 이 가지고 있는 레퍼런스가 삭제되지 않도록 해야함 .

void bug(PyObject* list){        PyObject* item = PyList_GetItem(list, 0);        PyList_SetItem(list, 1, PyLong_FromLong(0L));        PyObject_Print(item, stdout, 0);}

Page 18: 파이썬 스터디 9장

참조카운트 레퍼런스 소유권 법칙

NULL 검사

- NULL 인 경우 , Py_DECREF() 를 호출하면 어떻게 되나 ?

• 파이썬에서는 해당 레퍼런스가 NULL 인지를 체크하는 매크로 함수가 없음 . • Py_XINCREF( ) 와 Py_XDECREF( ) 는 내부에 NULL혹은 None 인지를 검사하고 NULL 일 경우 참조

카운트에 대한 작업을 수행하지 않는다 .

PyObject *item = NULL;...Py_DECREF(item);

PyObject *item = NULL;...Py_XDECREF(item);

Page 19: 파이썬 스터디 9장

확장 타입• C 를 이용해서 모듈 뿐만 아니라 , 파이썬의 리스트 같은 타입 작성 가능 .

• 파이썬에서 쓸수 있는 새로운 자료형을 C 에서 만들어서 쓸수 있다 .

• 타입 정의 • 타입내 인스턴스 변수 정의• 생성자 , 소멸자 정의 • 사용자 함수 정의

• 198p

Page 20: 파이썬 스터디 9장

ctypes

외부 라이브러리 호출을 위한 완전 간단한 방법이 있다 .

파이썬에서 제공하는 ctypes 모듈을 사용 .

- 파이썬의 외부 함수 인터페이스 (FFI) 라이브러리로 , 파이썬 2.5 부터 기본으로 포함되어 있다 . 윈도의 DLL 과 같은 동적 라이브러리에 있는 함수를 직접 호출할 수 있으며 , 다양한 C 자료형을 다루기 위한 인터페이스를 제공한다 . 이를 사용해 순수 파이썬 코드만으로 확장 모듈을 구현할 수도 있다 .

- 윈도우 환경의 printf 함수를 가져와서 사용하는 예제

- C 의 데이터 타입을 바로 쓸 수 가 있다 . (213P) - 윈도우의 메시지 박스도 ctypes 를 통해서 랩핑해서 사용할 수 있다 .

http://docs.python.org/library/ctypes.html

>>> from ctypes import * >>> printf = cdll.msvcrt.printf >>> printf("hello world\n") hello world 12 >>> printf.restype = None >>> printf("hello world\n") hello world

Page 21: 파이썬 스터디 9장

ctypes

ctypes type C type Python typec_bool _Bool bool (1)c_char char 1-character stringc_wchar wchar_t 1-character unicode stringc_byte char int/longc_ubyte unsigned char int/longc_short short int/longc_ushort unsigned short int/longc_int int int/longc_uint unsigned int int/longc_long long int/longc_ulong unsigned long int/longc_longlong __int64 or long long int/long

c_ulonglong unsigned __int64 or unsigned long long int/long

c_float float floatc_double double floatc_longdouble long double floatc_char_p char * (NUL terminated) string or Nonec_wchar_p wchar_t * (NUL terminated) unicode or Nonec_void_p void * int/long or None

Page 22: 파이썬 스터디 9장

ctypes

• 사용자 정의 라이브러리 연동하기 - 윈도우 / 리눅스에서 모듈 생성

윈도우

• C:\Windows\system32 폴더로 test.dll 파일을 복사합니다 .

• >>> from ctypes import *>>> libtest = cdll.LoadLibrary('test.dll')>>> print libtest.multiply(2, 2)

Page 23: 파이썬 스터디 9장

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