1) instantClinet 설치

  - http://www.oracle.com 에 가서 download에 있는 instant client 를 다운 받는다.

  - instant client는 버전이 여러가지가 있는데 오라클 서버와 버전이 같은 것으로 다운 받는다. (꼭 버전을 맞춰야 하는지는 확인 못함)

  - instantclient-basic-linux-x86-64-11.2.0.2.0.zip 을 다운 받는다. (리눅스가 몇 bit인지 확인)

  - instantclient-sdk-linux-x86-46-11.2.0.2.2.zip 을 다운 받는다.

  - basic을 먼저 푼 다음에 sdk를 풀면 instantclient_11_2 디렉토리가 생기고 그 안에 sdk 디렉토리가 생겨서 좀 더 깔끔하다.


2) cx_Oracle 패키지 다운

  - cx_Oracle은 파이썬에서 오라클을 사용할 수 있게 해주는 패키지이다

   - 우분투용 패키지는 없으므로 소스파일을 받는다


3) cx_Oracle 패키지 컴파일

  - ORACLE_HOME 환경 변수를 설정한다. ORACEL_HOME 환경 변수는 cx_Oracle을 빌드할 때만 필요하다.

     $> export ORACLE_HOME=instantclent_11_2_디렉토리위치

    -  다음 명령으로 심볼릭 링크를 생성한다.

     $> ln -s libclntsh.so.11.1 libclntsh.so


4) libaio 설치

   - libaio 라이브러리가 없으면 나중에 cx_Oracle을 사용할 때 에러가 발생한다. 

   - https://launchpad.net/ubuntu/+source/libaio/0.3.109-2ubuntu1 가서 libaio_0.3.109.orig.tar.gz 파일을 받아 컴파일, 인스톨을 수행한다.


 

5) cx_Oracle 빌드, 인스톨

  - cx_Oracle 디렉토리로 가서 다음 명령을 수행한다.

  $> python setup.py build

  $> sudo ORACLE_HOMEinstantclent_11_2_디렉토리위치 python setup.py install


6) 설치 확인

  - 파이썬에서 cx_Oracle을 사용해본다.

    >>> import cx_Oracle

    >>> con = cx_Oracle.connect("ID/암호@주소:1521/sid")



7) 한글이 깨지는 경우

 - DB에 저장된 한글 데이터가 깨져서 출력된다면 NLS_LANG 값을 세팅한다.


>>> import os;

>>> os.putenv("NLS_LANG", "KOREAN_KOREA.KO16KSC5601"); # 안되면 "UTF8"로 세팅


## 쿼리를 수행해  데이터베이스에서 레코드를 가져온 후에

>>> print unicode(result[3], "cp949")






WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  0개가 달렸습니다.
secret

파이썬 코드를 보다보면 가끔 함수 이름 위에 @로 시작하는 구문이 붙어 있는 것을 가끔 볼 수 있다. 

@verbose

def my_function():

print "hello, world."

만약 자바를 주로 사용하는 프로그래머라면 어노테이션(annotation)이랑 비슷한 것인가 하는 생각이 들 것이다. 하지만 미리 말하자면 파이썬의 데코레이터와 자바의 어노테이션은 많이 다르다. 


파이썬의 데코레이터는 그 이름에서 알 수 있듯이 기본적으로는 본체를 꾸며주는 역할을 한다. 위 예제에서는 my_function()함수가 본체에 해당한다. 즉, @verbose라는 데코레이터를 추가함으로서 본체인  my_function()에 뭔가가 더해지기는 했지만, 기본적으로  my_function()이 바뀌는 것은 아니라는 의미이다[각주:1]


파이썬에서 함수는 first citizen에 속한다. 이 말은 파이썬에서 함수를 다른 함수의 인자로 넘기거나 함수의 리턴 값으로 사용하는 것이 가능하며 매우 자연스러운 일이라는 뜻이다. 다음 코드와 같이 말이다.

def verbose(func):

print "Begin", func.__name__;

func();

print "End", func.__name__;



verbose()는 다른 함수를 인자로 받는다. 물론 C에서도 함수 포인터를 이용해 동일한 기능을 이용할 수 있다. 하지만, C는 어디까지나 포인터를 인자로 사용하는 것이지, 함수 자체를 인자로 사용하는 것은 아니다. 즉, C에서 함수는 first citizen이 아니다. 함수가 first citizen인 언어에서는 이를 이용한 다양한 기법들이 있으며, 이를 통해 다양하고 기발한 코딩이 가능하다. 여기서 설명하는  데코레이터도 그 중에 하나이다.


verbose()에 my_function를 인자로 넣으면 다음과 같은 실행 결과를 볼 수 있다.

>>> verbose(my_function)

Begin my_function

hello, world.

End my_function

간단한 예제이지만 우리는 이를 통해서 다른 함수를 인자로 받는 함수를 살펴보았다.  여기서 verbose()는 my_function() 자체의 동작을 변경시키지 않는다. 다만, my_function()의 앞 또는 뒤, 위 경우에는 둘 다,에 약간의 장식을 덧붙였을 뿐이다.


그런데, 이 방법은 엄밀히 말하면 my_function()에 장식을 붙인 것이라 하기 힘들다. 왜냐하면 우리가 호출한 함수는 my_function()이 아니라 verbose()이기 때문이다. 즉 클라이언트 입장에서는 my_function()에 장식이 덧붙여진 것이 아니라, 원래 저런 기능을 하는 verbose() 함수를 사용한 셈이다.


verbose()를 수정해서 이 문제를 해결해보자.

def verbose(func):

def new_func():

print "Begin", func.__name__;

func();

print "End", func.__name__;

return new_func;


verbose()에서  new_func()이라는  nested function을 정의한 다음에 마지막에 이 함수를 반환한다. 이것은 앞서 말한 바와 같이 파이썬에서는 함수가 first citizen이므로 이러한 코드는 전혀 문제가 없다. new_func()이 하는 일은 기존의 verbose()와 동일하다.


이제 바뀐 verbose()가 my_function()을 어떻게 장식해주는지 보자.

>>> my_function = verbose(my_function)

>>> my_function()

Begin my_function

hello, world.

End my_function

먼저  verbose()의 리턴값으로 my_function을 대체한다. 이렇게 하면 my_function()은 원래 기능과 함께  verbose()에서 제공하는 부가적인 기능까지 수행하는 새로운 함수가 된다. 즉,  verbose()가 my_function()에 장식을 달아준 것이다. 그리고 이 장식은 계속 유지된다.


파이썬의 데코레이터는 사실 위 기능을 좀 더 직관적이고 간결하게 명시할 수 있도록 해주는 syntactic sugar일 뿐이다. 이 글의 첫번째 코드와 같이 함수 정의 윗부분에 데코레이터를 달면(데코레이션) 우리가 위에서 했던 "my_function = verbose(my_function)" 부분을 따로 쓸 필요도 없어지고 코드의 의도도 명확해진다. 주의할 점은 데코레이터는 결국 함수, 좀 더 정확하게 말하면 callable object이므로 당연히 데코레이션을 하기 전에 해당 함수 또는 클래스를 정의해야 한다는 것이다.


클래스 이야기가 나온 김에 클래스로 데코레이터를 정의하는 방법을 살펴보도록 하자. 클래스를 함수처럼 호출되게 하려면 __call__() 맴버 함수를 정의하면 된다. 이를 이용해서 위  verbose()함수를 클래스로 바꿔보면 다음과 같다.

class Verbose:

def __init__(self, f):

print "Initializing Verbose."

self.func = f;


def __call__(self):

print "Begin", self.func.__name__

self.func();

print "End", self.func.__name__


@Verbose

def my_function():

print "hello, world."


print "Program start"

my_function();



앞서 사용했던 verbose()와 구별을 위해, 그리고 일반적인 코딩 규약에 따라 클래스 버전은 대문자 V로 시작한다. 코드 자체는 간단하기 때문에 설명할 것은 별로 없다. Verbose가 언제 초기화 되는지를 확인하기 위해 생성자에 메시지를 출력하도록 하였으며, __call__() 맴버 함수에서 return 부분은 필요치 않다. 이 예제를 파일로 저장한 다음 파이썬으로 실행해보면 다음과 같은 결과를 볼 수 있다. 여기서는 파일 이름을 decorator_test.py라 하였다.

$ python decorator_test.py

Initializing Verbose

Program start

Begin my_func

hello, world

End my_func


결과를 보면 알 수 있듯이 Verbose 클래스의 생성자가 먼저 실행된 다음에 프로그램의 메인 부분이 실행된다. 이를 통해 우리는  데코레이터가 함수 정의 단계에서 실행됨을 알 수 있다.  그 밖에는 앞에서 이야기 한 것과 동일하다.


마지막으로 인자를 갖는 함수에 대한 데코레이터를 만드는 방법에 대해 이야기해보자. 지금까지의 과정과 마찬가지로이것 역시 파이썬답게 간단하다. 우리가 할 일은 단지 '*args'와 '**kwargs'만 추가해주면 된다[각주:2].

class Verbose:

def __init__(self, f):

print "Initializing Verbose."

self.func = f;


def __call__(self, *args, **kwargs):  #여기 

print "Begin", self.func.__name__

self.func(*args, **kwargs);    #여기

print "End", self.func.__name__


@Verbose

def my_function(name):   #l 인자를 갖는 함수

print "hello,", name




참고 자료



  1. 이 정도 의미로 생각한다면 자바의 어노테이션도 동일한 것이라 볼 수도 있다. 하지만 자바의 어노테이션은 파이썬에 비해 자신이 원하는 대로 만들어 사용하기가 매우 어렵다. [본문으로]
  2. *args, **kwargs 는 파이썬에서 일종에 가변 길이 인자를 다루는 방법이라 할 수 있다. 개인적으로는 파이썬 문법 중에서 가장 난해한 부분이 이 *args, **kwargs라고 생각한다. 사실 어려운 내용은 아니지만 직접 쓸 일이 없다 보니 매번 까먹는 것 같다. [본문으로]

WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  3개가 달렸습니다.
  1. 잘 보고 갑니다.
  2. rakk4403 2016.01.01 21:10
    잘봤습니다. 좋은포스팅 감사합니다.
  3. 잘보고갑니다.
secret

log(1+x) 계산하기

Programming 2012. 7. 27. 17:39

The Endeavorlog(1+x) 계산에 대한 재미있는 글이 올라와서 간단히 정리해보고자 한다.

참고로 이 블로그는 알고리즘, 수학(확률, 통계), 기계 학습 등에 관한 좋은 글들이 자주 올라오는 곳으로, Zite에서도 자주 포스트가 추천된다. 


프로그래밍을 할 때, log(1+x)를 계산하려면 어떻게 해야할까? 당연히 log()함수를 이용해서 계산하면 된다.

파이썬을 쓴다면 다음과 같은 코드면 될 듯 싶다. (원본 포스트에서는 C++을 이용했다)


import math


def cal_log1px(x):

  return math.log(1+x)


문제는 위와 같이 하면 안되는 경우가 있다는 것이다. 어떤 경우에 안되는지, 왜 안되는지를 설명하기 전에 해결책(?)을 먼저 보자.


import math


def cal_log1px(x):

     y = 1+x

     z = y-1;

     if (z == 0.0) 

         return x;

     else: 

         return x*math.log(y)/z




코드가 길어진 것은 둘째치고 이해할 수 없는 코드들이 중간 중간 눈에 띈다.


첫 번째 이상한 분은

y = 1+x

z = y-1


이다.

"y" 야 그렇다 쳐도, z 변수는 왜 선언한 것일까? z값은 당연히 x와 같으므로 z 변수를 굳이 사용할 필요가 없다. 일반적인 경우라면.


만약 x가 매우 작은 수, 이를 테면 x=1e-20 이면 위 y와 z는 우리의 기대와는 전혀 다른 값을 갖는다.

실제로 파이썬 인터프리터에서 x,y,z 값을 계산해보면 다음과 같다.

>>> x = 1e-20

>>> y = 1+x

>>> z = 1-y

>>> x

9.9999999999999995e-21

>>> y

1.0

>>> z

0.0

>>>

>>> z == x

False

>>>



분명, y 값은 1.0 보다 커야 하며, z는 0.0이 아닌데도 불구하고 위와 같이 계산된다.

즉, 현재의 배정도 실수 변수가 처리할 수 있는 정밀도를 벗어나는 값을 계산할 경우에는 이와 같이 엉뚱한 일들이 일어난다.


위에서 제시한 (정답) 함수는 이러한 경우를 미리 체크해서 z==0.0이 되는 경우에는 매우 작은 값 x를 리턴함으로써 어느 정도 오류를 방지할 수 있다.

물론 log(1+x) != x 이지만, 어차피 둘 다 매우매우 작은 값이므로 적당한 approximation이라 할 수 있다.


아직 해결하지 못한 궁금점은, 그래서 댓글도 많이 달려 있는 의문은 왜 x*log(y)/z 로 계산하는가 이다.

z != 0.0 이라면 x가 계산 가능할 정도로 충분히 큰 값이라는 의미이므로 그냥 log(1+x)로 계산해보 되지 않을까?






'Programming' 카테고리의 다른 글

C++에서 UTF-8 사용  (3) 2012.09.06
log(1+x) 계산하기  (1) 2012.07.27
date 명령어[Linux/Unix] 사용  (0) 2012.07.26
Python을 이용한 노래 검색  (1) 2011.07.06

WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  1개가 달렸습니다.
  1. 생각해보니까 그게 아니었던 것 같다. 여전히 코드 상에 이해가 되지 않는 부분이 있다. 좀 더 파봐야 할 듯.
secret
개인적인 이유로 노래 검색을 구현하게 됐다.
목표는 간단하다. 임의의 개수의 "노래 + 가수" 리스트가 주어졌을 때, 이들 노래가 실제로 존재하는 노래인지를 확인하는 것이다.

노래 DB가 있다면 금방 끝날 일이지만, 가진 데이터가 없어서 웹 검색을 이용하기로 했다.
네이버 뮤직, 멜론과 같은 음악 전문 사이트에서 "<노래> <가수>"로  검색을 해서 검색 결과가 있다면 실제로 존재하는 노래, 없다면 존재하지 않는 노래로 판단한다. 

1. 대상 사이트 선정 및  쿼리 분석
검색어를 입력하면 이를 POST 방식이 아닌 GET 방식으로 서버에 전송하는 사이트를 찾는게 편할 것이다.
다행이 네이버 뮤직은 다음과 같이 GET 파라메터가 단순해서 이를 이용하기로 했다.
 한글 부분이 인코딩되서 '%EC' 이런 식으로 나오는데, 다음과 같이 주소창에다 직접 써도 된다.

http://music.naver.com/search/search.nhn?query=사랑했지만+김광석&x=0&y=0


 query 뒤에 붙는 x, y 인자는 뭔지 모르겠다. 항상 붙는 것 같다.

2. Python에서 해당 페이지 긁어오기
작성된 검색 URL을 이용해 결과 페이지를 긁어오는 코드를 작성한다.
Python의 기본 라이브러인 urllib를 이용하면, 아쉬 쉽게 된다. (난 파이썬의 이런 부분이 참 맘에 든다)
 

import urllib
f = urllib.urlopen("http://music.naver.com/search/search.nhn?query="+unicode(keyword,"euc-kr").encode("utf8")+"&x=0&y=0")

cont = f.read(); 

여기서 한가지 신경을 써 줄 부분은 (다른 곳도 그런지는 모르겠지만) 네이버 뮤직은 utf-8 인코딩을 사용한다는 것이다. 그래서 query 뒤에 붙는 한글 부분을 위와 같이 utf-8로 인코딩해서 붙여줘야 한다.

어쨌거나, 이제 cont에는 결과 페이지(HTML)가 들어 있다. print 해보면 쫙 출력된다.

3. 존재하는 노래인지 확인
네이버 뮤직에서는 없는 노래를 검색하면 "OOO에 대한 검색 결과가 없습니다"라는 메시지가 뜬다 (화면 캡쳐는 생략)
그래서 이 문장이 긁어온 페이지에 있는지 여부로 노래의 존재 여부를 판단하기로 한다.

data = unicode(cont,  "utf-8").encode("euc-kr", "ignore")

if ( data.find("검색결과가 없습니다.") == -1 ):

# 다음 처리 

else:

return None



긁어온 페이지는 utf-8로 되어 있지만, 나의 로컬 작업 환경은 euc-kr이므로, 긁은 내용을 euc-kr로 인코딩한다. 
그런 다음에 전체 페이지 어딘가에 "검색결과가 없습니다"라는 메시지가 있다면, 검색 실패이므로 None을 리턴한다. 만약 해당 메시지가 없다면 뭔가가 검색된 것이므로 다음 과정을 수행한다.

4. 니가 찾은 노래가 그 노래가 맞냐?
불행히도 검색 결과가 있다고 해서 그게 정확히 내가 원하는 노래는 아닐 수도 있다. 예를 들어 "제주도 푸른밤 성시경"이라고 입력해도 "제주도 푸른밤"이 검색되고, "없는 노래"를 검색하면 "가질 수 없는 너"가 검색되기도 한다.

따라서, 검색 결과를 대조해보는 작업이 필요한데, 문제는 검색 결과 페이지에 필요 없는 부분이 너무 많다는 거다. 그래서 검색 결과 페이지의 HTML 소스 코드를 분석하면서 패턴을 파악한 후에 다음과 같이 작업했다.
 1) 검색 결과를 리스트로 출력하는 부분을 찾는다. 네이버 뮤직에서는 "트랙 리스트"라는 주석 다음에 이 부분이 나온다.
 2) 노래 제목이 나오는 부분을 찾는다. "트랙 리스트" 다음에 ""_title title NPI="란 문구가 있는 부분이 이에 해당하는데, HTML 소스 코드로 보면 다음과 같이 되어 있다. 

<td class="album"><a href="/album/index.nhn?albumId=191356" title="현빈, 가질 수 없는 너" class="_album NPI=a:album,r:1,i:191356"><span class="ellipsis">현빈, 가질 수 <b>없는</b></span></a></td>


3) 제목을 추출한다. "title=" 뒤에 나온다.

이를 코드로 작성하면 다음과 같다.

pos = data.find("트랙 리스트")

if ( pos == -1 ):

return None

pos = data.find("_title title NPI=", pos);

pos = data.find("title=",pos+20)

pos2 = data.find("\"", pos+8)

return data[pos+7:pos2]



 
작성한 전체 코드는 다음과 같다.

import urllib

def crawl_song(keyword):

f = urllib.urlopen("http://music.naver.com/search/search.nhn?query="+unicode(keyword,"euc-kr").encode("utf8")+"&x=0&y=0")

cont = f.read();

data = unicode(cont,  "utf-8").encode("euc-kr", "ignore")

if ( data.find("검색결과가 없습니다.") == -1 ):

pos = data.find("트랙 리스트")

if ( pos == -1 ):

return None

pos = data.find("_title title NPI=", pos);

pos = data.find("title=",pos+20)

pos2 = data.find("\"", pos+8)

return data[pos+7:pos2]


else:

return None

 

Python은 확실히 이러한 간단한 작업을 하기에는 최적의 도구인것 같다. 다양한 라이브러리가 존재하기 때문에 외부에서 데이터를 얻어오는 것도 쉽고, 인코딩 문제도 가볍게 해결할 수 있다. 문자열 검색이나 일부를 추출하는 것도 전혀 어렵지 않다. 이 코드를 좀 더 확장해서 노래 리스트를 긁어오는 것도 매우 쉬운 작업이다.

사실 이 코드를 짜면서 가장 골치 아팠던 부분은 인코딩에 관한 것이었다.  웹페이지는 utf-8을 이용하고, 나는 euc-kr을 이용하기 때문에 여러 불일치가 발생했고, 이를 해결하기 위해 이렇게 저렇게 인코딩을 바꿔가면서 코드를 작성해야 했다. 하지만, 코드를 보면 알 수 있다시피 해결책은 매우 간단했다.

'Programming' 카테고리의 다른 글

log(1+x) 계산하기  (1) 2012.07.27
date 명령어[Linux/Unix] 사용  (0) 2012.07.26
Python을 이용한 노래 검색  (1) 2011.07.06
C++에서 다차원 배열을 함수 인자로 넘기기  (4) 2011.03.08

WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  1개가 달렸습니다.
  1. 태양이 바다에 미광을 비추면,나는 너를 생각한다.
secret