IT의 불모지 우리나라에서 꾸준히 사랑받아온 python3 책이 version up 되었습니다.

기존 내용 중 부족한 부분들도 여러군데 보강하였고, 유용한 모듈들도 상당수 추가되었습니다. 책을 받아보니 첫쇄보다 상당히 두꺼워졌네요~

항상 모든 일에 열정을 다하는 우상정선임, 최동진선임, 박찬규사장님 덕에 제가 항상 묻어갑니다.

http://www.yes24.com/24/goods/6687985?scode=032&OzSrank=1


AND

며칠 전, 강의+자문 요청이 들어왔다.

그 업체에서 자사 엔진 위에 사용자 변경이 가능한 script를 올리고 싶은데, script 언어들에 대한 대략적인 소개 및 비교를 하고, 요구사항들에 대한 가능성, 디자인 등에 대한 조언을 요청한 것이었다.

남 앞에 서는것도 별로 안 좋아하고 방콕 스타일인 나에겐 일상적이지 않은 일이기에 심사숙고를 하다가, 진행하기로 결정하고 오늘 다녀왔다.

첫번째 시간은 2시간 동안 진행되는 강의세션, 두번째 시간은 2시간 동안 해당 프로젝트에 대한 논의 세션으로 진행했다. 개인적으론, 양쪽 모두 원하는 바를 얻은 유익한 시간이었다고 생각하고 있는 중이다... :)

아래 자료는 내가 맡은 부분들에 대한 발표자료들이다. 언젠가 쓸모 있지 않을까 해서 slideshare에 올려 놓았다 ㅎㅎ




ps. C binding쪽의 발표를 맡은 최동진선임도 밤 늦게까지 수고 많이 하셨어요.



AND

주말마다 영어동의어 시험을 보는데, 공부할 시간은 여의치 않아서 시간날때 마다 체크해 보려고 초간단 시험보기 프로그램을 짜봤습니다.

>>> [db파일+소스파일 다운로드]

단순하게 sqlite db에 있는 question 필드에 있는 문제를 print하고, 사용자 입력을 받은 후 답을 찍어줍니다. 답을 맞춰도 되겠지만 별로 의미가 없어 보여서 맞춰보지는 않습니다.
sqlite db manager 프로그램을 이용해서 문제를 추가하시면 됩니다.

소스코드는 다음과 같이 12줄입니다. ㅋ
  1. #!/usr/bin/python
  2. import sqlite3, random
  3. con = sqlite3.connect("./words.db")
  4. cur = con.cursor()
  5. cur.execute("select * from data")
  6. res = cur.fetchall()
  7. while 1:
  8.     question, answer = random.choice(res)
  9.     print 'Question:', question
  10.     user_input = raw_input("--> ")
  11.     print 'Answer:',answer
  12.     if user_input.lower()=='q': break
AND

간만에 정규식에 대해서 정리할 일이 생겨서 블로그에도 정리해 봅니다. :)


정규식에는 greedy한 방식과 lazy(non-greedy)한 방식이 있습니다.

일예로 ".*"의 경우가 greedy한 방식입니다. 즉, 조건을 만족하는 한 가장 긴 문자열을 선택하려고 합니다.
그리고 반대의 경우, ".*?"의 경우가 lazy한 방식입니다. 조건을 만족하는 한 가장 짧은 문자열을 선택하려고 합니다.

음, 저도 써 놓고 보니 무슨 소리인지 모르겠네요 :)
예제를 살짝 바꿔서 돌려보면 차이를 이해하실 수 있을 것입니다.

아래와 같이 "<"과 ">"로 둘러싼 문자열을 greedy하게 선택해 보겠습니다.  (@python 2.6)

>>> import urllib
>>> html = urllib.urlopen('http://www.python.org').read()
>>> import re
>>> greedy = re.compile(r'<.*>', re.I|re.S)
>>> len( greedy.findall(html) )
1

엇, 결과 개수가 1개네요. 즉, <>로 둘러싼 가장 긴 문자열을 선택하려고 했으니 html 태그 전체를 선택하게 된 것을 보
실 수 있습니다.
이번에는 살짝 바꿔서 lazy하게 선택해 보겠습니다.

>>> lazy = re.compile(r'<.*?>', re.I|re.S)
>>> len ( lazy.findall(html) )
469

네, 이번에는 <>로 둘러싼 짧은 문자열을 선택했으니, 모든 태그들을 선택하게 되었습니다.


원문 : http://groups.google.com/group/python3/browse_thread/thread/c6c40a3738818d77
AND

사실 파이썬 책 기획을 할 때, 출판사 분들에게 장담한 것 중 하나가 "열혈강의 파이썬"을 이겨 보겠다는 것이었습니다. 그런데 사실 늦게 시장에 들어가서 1등을 이기기는 쉽지가 않죠. 저희 마저도 힘들지 않을까하고 생각했지만, 며칠전 드디어 역전을 했습니다. 아직은 yes24 상에서 근소한 차이로 이기고 있지만, 앞으로 몇번의 퀀텀점프 가능성이 있는 이벤트가 있으므로 보다 긍정적인 기대를 해도 좋지 않을까 생각합니다.

http://www.yes24.com/2.0/Category/CategoryMain.aspx?CategoryNumber=001001003016003005

새해 복 많이 받으세요~
AND


Neverendless 프리서버에서 와우를 시작하기는 했는데, 문제가 있었습니다. vote 템을 사기 위해서는 주기적으로-12시간마다- vote를 해야되는데, 인간적으로 저걸 위해서 맨날 사이트를 쳐다보는 짓은 못하겠어서, 간단하게 script를 만들었습니다.

이 script를 돌리기 위해서는 아래와 같은 것들을 설치해야 합니다.
- python 2.5 or 2.6 : http://python.org/download/
- ClientForm : http://wwwsearch.sourceforge.net/ClientForm/
- ClientCookie : http://wwwsearch.sourceforge.net/ClientCookie/

vote.py를 매시간 혹은 그보다 짧게 실행하다록 윈도우즈의 예약작업이나 cron을 설정해 놓으시면 됩니다. 그러면 vote.py는 최근 vote를 수행한지 12시간이 지나면 로그인해서 vote를 하게 됩니다.

login 아이디와 비번은 아래 부분을 수정하시면 됩니다.
11. form['login'] = '아이디'
12. form['pw'] = '비번'

binary로 배포할까 하다가, 비번을 빼가는걸로 오해하실 분들이 있으실지 몰라서 소스채로 오픈합니다.
그럼 즐거운 와우생활 하세요~

[Download vote.py]

  1. #/usr/bin/python
  2. import urllib2, time, traceback, pickle
  3. import ClientCookie
  4. from ClientForm import ParseResponse
  5.    
  6. def vote():
  7.     print 'try to vote'
  8.     url = "http://neverendless-wow.com/login/?=back=/usercp/voteshop/" 
  9.     forms1 = ParseResponse( urllib2.urlopen( url ) )
  10.     form = forms1[0]
  11.     form['login'] = 'INPUT YOUR ID HERE'
  12.     form['pw'] = 'INPUT YOUR PASSWORD HERE'
  13.     request = form.click()
  14.     response = ClientCookie.urlopen( request )
  15.     response = ClientCookie.urlopen('http://neverendless-wow.com/usercp/vote/')
  16.     forms2 = ParseResponse( response )
  17.     print 'the number of form detected :', len(forms2)
  18.     for form in forms2:
  19.         time.sleep(2)
  20.         try:
  21.             req = form.click()
  22.             response = ClientCookie.urlopen( req )
  23.         except:
  24.             #traceback.print_exc()
  25.             pass
  26.         else:
  27.             print 'clicked'
  28.             return
  29.    
  30. if __name__ == "__main__":
  31.     bVote = False
  32.     try:
  33.         f = open('time.pkl', 'rb')
  34.         prev = pickle.load(f)
  35.         f.close()
  36.  
  37.         curtime = time.time()
  38.         dur = curtime - prev
  39.         if dur > 12*60*60:
  40.             bVote = True
  41.         else:
  42.             print 'not ready yet'
  43.     except:
  44.         traceback.print_exc()
  45.         bVote = True
  46.  
  47.     if bVote:
  48.         vote()
  49.  
  50.         f = open('time.pkl', 'wb')
  51.         curtime= time.time()
  52.         pickle.dump(curtime, f)
  53.         f.close()
AND

와우~, 떡이떡이님이 저희 책을 소개해 주셨네요.

"C언어보다 파이썬이 먼저 발표됐다면 세상이 아름다워졌을텐데"
http://itviewpoint.com/142711


이렇게 좋게 소개해 주시다니 감사합니다.

그나저나, 역시 최동진씨의 멘트는 심상치 않아요.
AND

Train Timetable

개발/python 2009. 8. 22. 01:03
이번에는 두번째 문제인 기차시간표입니다.

문제는 다음과 같습니다.


제일 가까운 기차들을 모두 이어주고,
앞뒤로 이어지지 않은 기차들을 카운트해주면 됩니다.

소스코드는 다음과 같습니다.

  1. import sys
  2. import datetime
  3.  
  4. class train:
  5.   def __init__(self, row, dir):
  6.     st = row.split(' ')[0]
  7.     end = row.split(' ')[1]    
  8.     self.start = datetime.datetime( 2009,1,1, int(st.split(':')[0]), int(st.split(':')[1]) )
  9.     self.end = datetime.datetime( 2009,1,1, int(end.split(':')[0]), int(end.split(':')[1]) )
  10.     self.dir = dir
  11.     self.reserved1 = 0
  12.     self.reserved2 = 0
  13.  
  14. def asc_start(a,b):
  15.   if a.start < b.start:
  16.     return -1
  17.   else:
  18.     return 1
  19.  
  20. def desc_end(a,b):
  21.   if a.end < b.end:
  22.     return 1
  23.   else:
  24.     return -1
  25.  
  26. if __name__=="__main__":
  27.   if len(sys.argv)>1:
  28.     inp = sys.argv[1]
  29.   else:
  30.     print "append an input file param"
  31.     sys.exit()
  32.  
  33.   f = open( inp, 'rt' )
  34.   nTC = int( f.readline() )
  35.   print 'the number of tc :', nTC
  36.  
  37.   output_file = inp.split('.')[0]+"_output.txt"
  38.   fout = open( output_file, 'wt')
  39.  
  40.   for i in range(0,nTC):
  41.     turnaround = int( f.readline() )
  42.    
  43.     NA_NB = f.readline()
  44.     NA = int( NA_NB.split(' ')[0] )
  45.     NB = int( NA_NB.split(' ')[1] )
  46.    
  47.     trains = []
  48.     for n in range(0,NA):
  49.       row = f.readline()
  50.       trains.append( train( row, 'A' ) )
  51.     for n in range(0,NB):
  52.       row = f.readline()
  53.       trains.append( train( row,'B') )
  54.  
  55.     trains.sort(desc_end)
  56.    
  57.     #process
  58.     ta = datetime.timedelta( 0, turnaround*60 )
  59.    
  60.     for t1 in trains:
  61.       if t1.reserved2>0: continue
  62.       cands = []
  63.       for t2 in trains:
  64.         if t2.reserved1>0: continue
  65.          
  66.         if ( t1.end + ta <= t2.start ) and (t1.dir != t2.dir):
  67.           cands.append( t2 )
  68.          
  69.       if cands:
  70.         cands.sort( asc_start )
  71.         min = cands[0]
  72.         #print t1.start, t1.end, t1.dir
  73.         #print '\t',min.start, min.end, min.dir
  74.        
  75.         t1.reserved2 = min.reserved1 = 1
  76.  
  77.     NA = NB = 0
  78.     for t in trains:
  79.       if t.reserved1==0:
  80.         if t.dir=='A':
  81.           NA+=1
  82.         else:
  83.           NB+=1
  84.  
  85.     # result
  86.     print 'Case #'+str(i+1)+':' ,NA, NB
  87.     fout.write( 'Case #'+str(i+1)+': ' +str(NA) + ' '+str( NB)+'\n')
  88.  
  89.   f.close()
  90.   fout.close()


실행결과는 다음과 같으며, 모두 Correct입니다.


AND

Saving the Universe

개발/python 2009. 8. 21. 01:48
요즘 기분이 꿀꿀해서 CodeJam 2008 Qualification Round 문제 중 하나인, 'Saving the Universe'를 풀어봤습니다.

1번 문제(Saving the Universe)는 다음과 같습니다.
문제링크 : http://code.google.com/codejam/contest/dashboard?c=agxjb2RlamFtLXByb2RyEAsSCGNvbnRlc3RzGI36AQw#

그냥 간단하게 검색엔진 list를 둬서 카운트 하다가 꽉차면 다시 비우고를 반복하면 끝입니다.
파이썬 코드는 다음과 같습니다. (2.5.4버전으로 했음)

  1. import sys
  2.  
  3. def isAllChecked(d):
  4.   ret = True
  5.   for k in d.keys():
  6.     if d[k]<1:
  7.       ret = False
  8.   return ret
  9.  
  10. if __name__=="__main__":
  11.   if len(sys.argv)>1:
  12.     inp = sys.argv[1]
  13.   else:
  14.     print "append an input file param"
  15.     sys.exit()
  16.  
  17.   f = open( inp, 'rt' )
  18.   nTC = int( f.readline() )
  19.   print 'the number of tc :', nTC
  20.  
  21.   output_file = inp.split('.')[0]+"_output.txt"
  22.   fout = open( output_file, 'wt')
  23.  
  24.   for i in range(0,nTC):
  25.     nEngine = int( f.readline() )
  26.     engines = []
  27.     for e in range(0,nEngine):
  28.       engine = f.readline()
  29.       engines.append( engine )
  30.     nKeyword = int( f.readline() )
  31.     keywords = []
  32.     for k in range(0,nKeyword):
  33.       keyword = f.readline()
  34.       keywords.append( keyword )
  35.    
  36.     # process
  37.     dEngine = {}
  38.     for e in engines:
  39.       dEngine[e]=0
  40.     nCount = 0
  41.     for k in keywords:
  42.       if k in dEngine.keys():
  43.         dEngine[k] += 1
  44.         if isAllChecked(dEngine):
  45.           for e in engines:
  46.             dEngine[e] = 0
  47.           nCount += 1
  48.           dEngine[k] += 1
  49.          
  50.     # result
  51.     print 'Case #' + str(i+1)+': '+str( nCount )
  52.     fout.write( 'Case #' + str(i+1)+': '+str( nCount ) + '\n' )
  53.      
  54.   f.close()
  55.   fout.close()

실행결과는 다음과 같으며, 둘다 Correct 입니다.


AND

요즘 뜨는 SNS인 트위터를 회사에서 모니터링하기에는 너무 눈치가 보여서, 브라우저 플러그인이나 독립어플이 아닌 커멘트라인으로 어떻게 바꿀 수 있을까 생각 중이었는데요. 트위터는 open api가 잘 되어있고, python-twitter 모듈이 쉽게 정리되어 있어서 구현이 정말 쉽더군요.

특히 왜 이렇게 스팸들이 많은지 이해할 수 있었습니다. 모니터링하면서 following하고 스팸 뿌리기가 너무 쉽네요 --;

뭐, 하여간 회사에서 약간 투명하게 커맨드라인 설정해 놓고 쓰기 적당하게 대충 짜 보았습니다. 소스 링크는 다음과 같습니다.
[peeping_tweet.zip]
소스는 보시다시피 거의 python-twitter 그대로입니다 --;

peepingtweet.py를 열어보시면 소스 상단에,
login_id = ""
login_password = ""

과 같이 되어있는데요.
이렇게 되어 있는 경우에는 아래 스샷과 같이 public tweet들이 보이게 됩니다,



자신의 친구들 글을 보려면 login_id와 login_password에 적당한 값을 넣으면 아래 스샷과 같이 제대로 나오게 되는 것을 확인할 수 있습니다.


회사에서는 우분투에서 돌려야지... --;

ps1. python 2.5 기반입니다.
그러므로 혹시나 파이썬이 안깔려있다면 까셔야합니다.
http://python.org/download/releases/2.5.4/

ps2. py2exe는 패스...
아, 그리고 이건 모니터링 온리입니다. 글 올리는 기능은 없습니다.
물론 넣기는 쉽겠지만, 제가 그러한 요구사항을 못 느껴서요 ...
필요하신 분들은 걍 추가해서 사용하세요...
AND