파이쿠차 -오랜만의 파이썬 마을 모임!

다음 주(7월 27일-8월 2일)에 오랜만에 애자일 개발 워크샵 일로 1주일 동안 서울에 머물게 됐습니다. 그래서 서울 방문 기념으로 거의 1년 만에 파이썬 마을 모임을 한 번 하려고 합니다.

지난 번에 다른 행사의 부대행사로 파이썬 관련 세션을 하나 생각하면서, 자기가 파이썬을 쓰는 분야에 대해서 얘기하고 토론하는 걸 좀 생각해 봤었는데요. 최근에 페차쿠차라는 걸 보고 한 번 "내가 파이썬을 쓰는 곳/파이썬으로 만든 것"을 페차쿠차 형식으로 한 번 해 보면 어떨까 생각했습니다.

페차쿠차는 발표 포맷이 정해져 있는데요, 슬라이드 20장을 각 20초 씩 해서 총 6분 발표를 해야합니다. 보통 많은 사람들이 발표하는 곳 가면 거의 반 정도는 발표하는 사람을 제발 끌어내리고 싶은 유혹을 받는데 그런 걸 줄이기 위한 포맷이라고 하네요. 그래서 이번에는 20장은 보통 엄청 오래 한 분 아니라면 회사 기밀까지 나와야할 것 같아서, 12장 * 20초 해서 4분으로 하면 좋을 것 같네요. 중요한 것은 슬라이드는 자동으로 넘어가니까 한 슬라이드에서 시간을 마음대로 조절해서 쓸 수는 없다는 게 페차쿠차의 기본 원리라고 합니다. (다양한 분야에 활용하시는 분들이 참석하셔서 경험을 나누시면 좋겠습니다!)

혹시 장소나 시간에 대해서 좋은 생각이나 제안 (또는 장소제공) 등 남기실 말씀이 있는 분들은 알려주세요. ^^ 대략 인원 추산도 해야하니 참석하실 수 있는 분들 답글 남겨주시면 고맙겠습니다~

댓글 15 개 | 트랙백 0 개 (보낼곳) | 태그 python


인스턴스 복사를 직접 다루기

종종 한 인스턴스를 대량으로 복제해서 약간씩 바꿔야할 일이 있습니다. 주로 진화 알고리즘 계열이나 고치기 전/후를 비교해야하거나, 엄청나게 바꾼 뒤에 롤백을 해야한다는 등의 경우가 그런데요. 파이썬에서 복사는 기본 타입들은 각각 독특한 방법이 있지만, 보통은 copy모듈을 쓰면 간단합니다.

>>> x; copy.copy(x)
<__main__.X instance at 0x83992ec>
<__main__.X instance at 0x839970c>

그런데 철학적(?)인 분위기를 잡으며 경험이 조금 더 많은 사람이 쓱 나타나서 "너 깊은 복사랑 얕은 복사 알아?"라고 할 것 같은 느낌이 언제나 copy를 쓸 때마다 들곤 하죠; 깊은 복사(deep copy)는 참조하고 있는 객체 모두를 복사하기 때문에 속성으로 들어있는 놈들까지 별개의 인스턴스가 돼서 완전히 떨어집니다. 얕은 복사에서는 참조는 복사하지 않고 자신만 복사하기 때문에 리스트같은 것들이 공유가 되는 특징이 있습니다. 물론 얕은 복사가 훨씬 빠르고 자원을 효율적으로 쓰겠죠.

그런데 종종 클래스를 만들다 보면 어떤 것은 깊숙히, 어떤 것은 얕게 해야할 일이 발생하는데요. 그러면 copy.copy를 쓸 수도 없고 copy.deepcopy를 쓸 수도 없고 애매한 상황이 됩니다. 결국은 얕은 복사를 한 다음에 수동으로 깊은 복사를 해 주면 되겠죠. 그래서 좀 더 복사 과정을 자세히 다뤄보도록 하겠습니다.

우선 복사를 하려면 인스턴스를 새로 하나 만들어야 합니다. 여기서 발생하는 문제! __init__가 호출되면 복사라고 부르기가 애매하겠죠. 이미 초기화된 놈을 다시 초기화하게 되니까.. 그래서 __init__는 호출하지 않고 그냥 객체만 생성하는 방법을 써야하는데, 신형 클래스(new style class)냐 구형 클래스(old style class)냐에 따라서 방법이 다릅니다.

신형 클래스는 __new__를 쓰면 됩니다.

>>> class S(object):
...     def __init__(self): print "YAY!!!"
... 
>>> s = S()
YAY!!!
>>> s
<__main__.S object at 0x8399bec>
>>> s.__new__(S)
<__main__.S object at 0x8399b2c>

구형 클래스에서는 types.InstanceType을 쓰거나 types 모듈 불러오기가 귀찮으면 type(s)를 써서 만들 수 있습니다.

>>> class S:
...     pass
... 
>>> s = S()
>>> type(s)(S)
<__main__.S instance at 0x8399aac>
>>> s
<__main__.S instance at 0x8399a6c>

그래서 일부만 복사하는 놈을 만들어 보면

>>> class S(object):
...     def copy(self):
...             newobj = S.__new__(S)
...             newobj.a = self.a[:]
...             newobj.b = self.b
...             return newobj
... 
>>> s1 = S()
>>> s1.a = [1,2,3,4,5]
>>> s1.b = [6,7]
>>> s2 = s1.copy()
>>> s2.a[0] = -1
>>> s2.b[0] = -2
>>> s1.a, s1.b
([1, 2, 3, 4, 5], [-2, 7])
>>> s2.a, s2.b
([-1, 2, 3, 4, 5], [-2, 7])

이렇게 a는 복사되고, b는 공유되는 방식 copy()를 만들 수 있습니다.

사실 파이썬 자체에서 pickle 모듈같은 곳에서 저장/복원을 위한 속성 저장법이 있는데, copy 모듈도 같은 방법을 지원합니다.

>>> class S(object):
...     def __getstate__(self):
...             return self.a, self.b, self.c
...     def __setstate__(self, value):
...             self.a, self.b, self.c = value[0], value[1][:], value[2].copy()
... 
>>> s1 = S()
>>> s1.a, s1.b, s1.c = [1,2], [3,4], {'spam': 1, 'egg': 2}
>>> s2 = copy.copy(s1)
>>> s1.a[0] = s1.b[0] = -7
>>> s1.c['ham'] = 3
>>> s2.a, s2.b, s2.c
([-7, 2], [3, 4], {'egg': 2, 'spam': 1})
>>> s1.a, s1.b, s1.c
([-7, 2], [-7, 4], {'egg': 2, 'ham': 3, 'spam': 1})

__getstate____setstate__ 중 하나만 구현해도 상관은 없는데 그렇게 하면 리턴값이나 받아오는 값이 모두 딕셔너리가 돼야합니다. 위에서는 a는 그대로 참조만 가져오고, bc는 복사하는 방식으로 클래스 복사 방법을 지정해 줬습니다. 복사와는 크게 관련은 없지만 pickle에서 저장할 수 없는 속성들 (예를 들어 소켓 같은 것)이 들어있는 클래스는 __getstate__를 지정해 주는 방식으로 특정 속성을 저장에서 뺄 수 있습니다.

댓글 1 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 3의 한글

따로 말은 필요없고 직접 예제로!

>>> 합계 = sum
>>> 딸기, 바나나, 오이 = '딸기', '바나나', '오이'
>>> 가격 = {딸기: 300, 바나나: 500}
>>> 가격[오이] = 450
>>> 합계(가격[물건] for 물건 in [오이, 딸기, 딸기, 바나나, 오이, 바나나, 딸기])
2800
>>> 가격
{'딸기': 300, '오이': 450, '바나나': 500}
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', '가격', '딸기', '바나나', '오이', '합계']
>>> '전생에 백수였습니다'[2:7]
'에 백수였'

캬~~

관련 참조:

댓글 13 개 | 트랙백 0 개 (보낼곳) | 태그 python


"파이썬은 멀티코어 줘도 쓰잘데기가 없나요?"에 대한 파이썬 2.6의 대답

바야흐로 초딩도 멀티코어로 오락하는 시대가 오면서 이제 파이썬 GIL 공포가 많은 사람들을 위협하고 있었습니다. 그에 대한 해결책으로 예전부터 병렬 프로그래밍을 위한 여러가지 프레임워크, 예를 들어 MPI, CORBA, PyRO 같은 것들이 나왔지만, 다들 멋있게 모든 걸 포용하는 라이브러리를 만들다 보니 설치가 어렵거나 배우는데 한참 걸리는 게 결국 문제가 돼서 실제로 심각한 개발자 아니면 그냥 "CPU 1개면 충분해요"라고 눈을 반짝거리며 스스로 최면을 걸고 있었던 것이 사실입니다~

그래서 GIL의 파이썬 프로젝트 자체에서의 해결책으로는 공식적으로 결론에 도달한 것은 아니지만 Adam Olsen의 GIL없애기 프로젝트 같은 것도 있었는데, 이번에 몇몇 사람들의 강력한 후원으로 파이썬 2.6과 3.0부터 pyProcessing이 새 이름 multiprocessing 으로 표준 라이브러리로 들어오게 되었습니다.

pyprocessing의 다른 병렬 처리 라이브러리들에 대한 장점은 뭐니뭐니해도 표준 threading 모듈과 API가 같다는 점이겠죠. threading으로 기존에 짜 놓은 프로그램을 그냥 모듈 이름과 클래스 이름 아주 약간만 바꿔주면 쓰레딩 대신 멀티프로세싱을 사용하게 되어서 결국에는 멀티코어를 제대로 쓰는 프로그램이 됩니다. 실제로 쓰레딩같이 모든 변수를 공유하는 것은 아니고, 리턴값만 전달을 받기 때문에 정확히는 좀 다르다고 볼 수도 있지만, 뭐 "그렇게 짜면 왕변태"라고 선언하면 되겠죠. ㅎㅎ;

그래서 실제로 쓰는 모양을 보려고 옛날에 유행했던 정규식으로 소수 검사를 하는 걸 한 번 돌려 봤습니다.

from multiprocessing import Process as worker
import re, time

def isprime(n):
    convert = ''.join('1' for i in xrange(n))
    return not re.match(r'^1?$|^(11+?)\1+$', convert)

serial_st = time.time()
for i in range(10):
    isprime(1234567)
print 'Serial:', time.time() - serial_st

parallel_st = time.time()
processes = [worker(target=isprime, args=(1234567,)) for i in range(10)]
[p.start() for p in processes]
[p.join() for p in processes]
print 'Parallel:', time.time() - parallel_st

보시면 threading으로 만든 모듈과 join, start, worker 등의 사용법이 같아서 그냥 쉽게 바꾸실 수 있는데, 이렇게 돌려보면 대략 시간이 듀얼코어에서는 이렇게 나옵니다.

Serial: 18.2321028709
Parallel: 8.54528999329

대략 2배 정도 빨라졌죠~ 그냥 고전적인 던져주고 실행해서 리턴받는 방식 말고 보통 실제로 더 많이 쓰는 Queue 모델로 일꾼을 코어 개수만큼 돌려보면 이렇게 할 수도 있습니다.

from multiprocessing import Process, Queue
import re, time

q = Queue()

def isprime(n):
    convert = ''.join('1' for i in xrange(n))
    return not re.match(r'^1?$|^(11+?)\1+$', convert)

def primeworker():
    while True:
        n = q.get()
        isprime(n)
        print time.time() - parallel_st

parallel_st = time.time()
processes = [Process(target=primeworker) for i in range(2)]
[p.start() for p in processes]
for i in range(10):
    q.put(1234567)
[p.join() for p in processes]

시간은 대략 9초 정도 듭니다. 그리고 fd 넘겨주기를 지원하는 플랫폼들에서는 소켓도 받아다가 넘길 수가 있으니 네트워크 프로그램도 간단하게 멀티코어를 쓸 수 있습니다.

아주 간단하게 멀티코어를 쓸 수 있는 장점으로 표준 라이브러리로 도입이 되긴 했지만, 아직 문제가 몇 개 있는데요. 대표적으로 FreeBSD에서는 아직 POSIX 1003.1b 세마포어를 "제대로" 지원하지 않기 때문에 FreeBSD에서는 Queue나 Lock등과 관련된 것들을 하나도 쓸 수가 없습니다. (위 예제는 그래서 리눅스에서 테스트할 수 밖에 없었습니다;;)

그 외에 MPI 등의 "심각한" 분산 API들을 쓰던 프로그래머들은 이게 애들 장난이냐 하면서 없는 기능들을 지적할 수도 있는데, 아무래도 표준 라이브러리로써 아주 간단하고 기초적인 기능만 제공하는 것이 목적이고, 셋업이 복잡하거나 거대 프레임워크를 끌고 다니는 경우라면 표준에서 안정적으로 관리하기가 힘들겠죠. 그래서 multiprocessing을 도입하자 주장한 개발자는 이 모듈은 절대 다른 분산 관련 모듈을 쓰지 말라는 의미가 아니고 같이 쓰면 더욱 좋다고 강조합니다.

댓글 5 개 | 트랙백 0 개 (보낼곳) | 태그 python


동영상(?)으로 보는 파이썬의 역사

Michael Ogawa의 code_swarm이라는 프로젝트에서 오픈소스 프로젝트의 CVS나 SVN 등 소스 저장고를 분석해서 일어난 작업들을 시각적으로 보여주는 걸 만들었습니다. 요새 UbiGraph같은 도구도 나오고 하는 걸 보면 시각화가 정말 굉장히 유행이네요~


code_swarm - Python from Michael Ogawa on Vimeo.

저는 3분 50초 쯤에 약간 왼쪽 아래에서 나타나서 잠시 머물다가 사라집니다;;; -ㅇ-;;;;;

이 영상은 미디어아트와 데이터 시각화에서 많은 관심을 모았던 Processing으로 만들었다고 하는데.. 전에 봤을 때는 괜히 어렵기만 하고 별 쓸모 없어보니더니 이걸 보니 확 땡기는군요. ^^;;

-- via Python Daily URL

댓글 10 개 | 트랙백 0 개 (보낼곳) | 태그 python computer


"내 이름 어때" 만든 이야기~

며칠 전에 올렸던 "내 이름 어때?"를 만들면서 썼던 여러 가지 기술적인 부분에 대해서 간단하게 정리해 봅니다. 물론 django로 만들었습니다! 이히히

Django 템플릿에서 한글 조사 처리

이름 뒤에 은/는 이/가 같은 것들을 제대로 붙이려면 아무래도 템플릿에서 처리를 해 줘야하는데, django에서는 애플리케이션에서 직접 템플릿 태그나 필터를 정의하는 걸 매우 장려하는 분위기라서 "필터"를 따로 정의해서 처리했습니다.

템플릿에서 이렇게 쓰려고 하는 부분이 있다면:

이름을 뒤집으면 {{ rev_last_name|attach_korean_suffix:"이가" }}
되어서..

필터 정의를 이렇게 해 줬습니다.

@register.filter
def attach_korean_suffix(string, suffixes):
    if hangul.ishangul(string[-1]) and hangul.split(string[-1])[2]:
        return suffixes[0]
    else:
        return suffixes[1:]

마지막 줄에서 1:로 굳이 잡아준 이유는 이름 뒤의 ~이 처럼 받침이 없으면 끝에 안 붙는 경우도 처리해 주려고요..

추세 해석

이름의 인기가 늘고 있는지 줄어드는지를 글자로 판단해서 표현해 주기 위해서, 간단한 계산식을 사용했습니다. 우선 원 데이터 자체는 샘플수가 적어서 노이즈가 많기 때문에 보통 많이 쓰이는 9개 윈도우 평균으로 했고, 이렇게 하면 18개 포인트가 나와서 세 부분으로 나눠서 앞 중간 뒤의 평균을 다시 구해서 3가지 값이 나왔습니다. 그래서 눈으로 딱 보면 값이 계속 증가하는지, 올라갔다 내려갔다 하는지를 볼 수 있는데요, 그냥 값으로 볼 수는 없으니 앞/중간 과 중간/뒤의 각각의 변동폭을 0에서 1사이로 정량화해서 봤습니다. 변동폭은 이름마다 절대량이 다르기 때문에 상대량으로 비교해야해서 아래와 같은 식으로 썼습니다.

\delta = {{\arctan {\log {N_B \over N_A}}} \over \pi} + 0.5

보기엔 약간 쓸데없이 복잡하긴 하지만, 그냥 상대비율을 (0, 1) 사이로 넣어주는 일 밖에 안 합니다;;;

이렇게 나온 값으로 앞 뒤가 모두 (0.4, 0.6) 구간에 들어오면 "꾸준한 추세입니다."라고 하거나, 앞-중간은 (0.0, 0.3), 중간-뒤는 (0.0, 0.5) 구간에 들어가면 앞 반쪽에서 감소세가 강하고 뒷 반쪽에서 감소세가 둔하다는 의미이므로 "확 줄어들다가 잦아드는 추세입니다."라고 보여주는 식으로 주된 패턴들을 "대충" 느낌으로 나열하는 방법으로 코딩했습니다. 크흐;

구글 차트

이름 전체의 성별 성향이나 이름의 시대적 경향, 이름 글자의 시대적 경향을 보이는 부분에서 구글 차트를 불러서 사용했습니다. 구글 차트는 직접 URL을 코딩하는 방법은 아니고, pygooglechart를 사용했는데요, 이게 의외로 그런대로 잘 만들어서 웬만한 기능은 불편없이 쓸 수 있게 돼 있더군요. :)

다만, 하나 기술적인 문제가 있었던 부분은 이름 글자의 시대적 경향 같은 경우에는 글자마다 실수값 18개씩(경향)이 저장돼야 하기 때문에, 이걸 그냥 저장하는 건 여러모로 번거롭고 해서 구글 차트 API에서 쓰는 0~4095 사이 인코딩하는 방법으로 썼습니다. (base64와 거의 같은 방법입니다.) 그래서, 저장은 바로 구글 차트 API URL에 쓰면 되는 형태가 돼서 다시 불러올 때 매우 빠르게 불러올 수 있긴 한데, 문제는 한 이름 안에 이름 앞자와 뒷자의 경향을 모두 보여줘야하기 때문에 둘의 그래프 크기를 제대로 조절해 주지 않으면 각 글자의 크기가 잘못 나온다는 점이었습니다.

그래서 결국 선택한 방법은 보여줄 때 앞자 뒷자 인코딩된 값을 다시 풀어서 큰 쪽의 스케일로 맞춘 다음에 다시 인코딩하는 -.,-; 약간 노가다성 방법을 썼습니다. 역시 이런 부분은 numpy의 array의 도움을 많이 받을 수 있었습니다.

확장코드 인코딩/디코딩 부분을 따로 떼서 쓸 일이 좀 있을 것 같아서..

encmap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'
decmap = [(encmap.index(chr(c)) if chr(c) in encmap else 0) for c in range(128)]
def decode_ggenc(encoded):
    return [decmap[ord(hi)] * 64 + decmap[ord(lo)]
            for hi, lo in zip(encoded[::2], encoded[1::2])]
def encode_ggenc(data):
    return ''.join(encmap[int(v//64)]+encmap[v%64] for v in data)

통계치가 적은 이름의 성별 추정

역시 통계 샘플 크기가 작아서 주요 이름들을 빼고는 제대로 된 통계치를 낼 수 없어서 주요 이름들의 성별 경향으로 학습한 걸로 예측하는 부분이 필요했습니다. 이번에는 아예 사용된 적 없는 글자까지도 어떻게 좀 해 보려고 통계치에 전혀 의존하지 않고 그냥 자소별로 분해해서 이름만 피처로 사용하기로 했습니다. 그래서 보통 SVM을 쓰는 것이 여러모로 대세이기는 하지만, 카테고리성(이산) 피처값에 매우 유리한 random forest을 썼습니다. (물론 제가 수학을 워낙 못하는 것도 큰 요인으로;;;;)

Random forest는 아무래도 쓸 수 있는 구현이 적다는 게 큰 문제인데요. 파이썬에서 쓸 수 있는 orange를 쓰면 정말 좋겠지만, 아쉽게도 이 구현은 리그레션은 지원하지 않고요. Y.Y R용 패키지인 partyrandomForest 중에 선택해야하는데, party를 먼저 했으나 메모리 3기가를 먹더니 죽었고 (-_-) randomForest는 안정적으로 대략 200메가 정도 먹고 그런대로 쓸 만한 결과를 줬습니다. :)

학습 기법 측면에서는 남자 샘플이 2배 정도 되기 때문에 편향 문제가 있어서 샘플링 조절을 좀 해야했는데요, 그냥 복잡한 것 쓰지 않고 대략 0.3 밑을 반 다운 샘플링하니까 전체적으로 분포가 윗쪽하고 아랫쪽이 그런대로 맞았습니다. 중성적인 이름이 수가 훨씬 적은 것도 또한 중성적 이름 쪽에서 오차를 많이 발생시킬 수 있는 요인이 될 수 있는데, 이쪽에서 오버샘플링을 하려고 하다가 "될 거 같으면 대충해도 돼야 하는거지" 하는 교수님 말씀이 귓가를 스치며 놀이인데 대충하자 하고 -ㅇ-;; 크흐; 그래서 결국 10-fold cross validation으로 평균 피어슨 연관성이 0.97 정도 나왔습니다. (만... 역시 사람 느낌하고 좀 다른 사례가 개별적으로는 제법 많이 발견되긴 하네요;)

페이지 내용 캐시

서비스를 공개한 다음 날 점심시간이 좀 지나고 나서는 접속이 폭주해서, 실시간 계산이 상당히 있었던 구현 특성상 앞으로 어떻게 될 지 참 고민이 있었는데요; 그래서 마침 전혀 필요없겠다 싶어서 꺼놨던 django의 캐시 프레임워크를 살려서 해 봤습니다. 백엔드를 선택할 수 있는데, 역시 제일 잘 나가는 memcached를 썼습니다. 이거 소문대로 깔끔하고 잘 돌아가네요. ^_^;

Django는 다행히도 템플릿에서 일부만 특정 변수에 따라서 캐시하는 기능이 있어서 이름에 따라 바뀌는 부분, 성에 따라 바뀌는 부분을 따로 따로 캐시하도록 3조각으로 따로 캐시해서 생각보다 훨씬 간단하게 쉽게 캐시로 넣었고요, 지금은 CPU부하가 전보다 같은 요청에서 거의 1/10로 줄어들었습니다! 이히히.

다른 사소한 것들..

몇 분께서 물어보셨던 게 자료처리나 통계처리는 어떤 걸 썼느냐가 있는데, 특별히 쓴 것은 없구요, 파이썬 하나면 다 해결됩니다. -ㅇ-; 물론 numpy, matplotlib도 아주 큰 도움이 됐습니다. collections.defaultdict를 전에는 그렇게 자주 쓰지는 않았는데, 이번에 좀 과격하게 3~4 단계 쑥쑥 defaultdict를 겹쳐서도 써 봤더니 pickle이 잘 안 되는 문제만 빼고는, 코드를 아주 많이 줄여준다는 점에서 아주 사랑스러웠습니다.

후속편으로는 이번에 들어온 로그를 한 번 분석해 보려고 하고 있습니다. ^^;

댓글 4 개 | 트랙백 0 개 (보낼곳) | 태그 happyhacking python computer


이터레이터 팁 몇 가지

나중에 쓰려고 쌓아두고 있던 간단한 예시인데, 더 이상 쌓이지가 않아서 (--;) 방출해 봅니다;

파이썬 이터레이터/제너레이터는 함축적으로 프로그래밍하는 걸 즐기는 사람들에게는 정말 재미있지만, 절차적 프로그래밍을 하다가 갑자기 이터레이터를 쓰는 게 적응이 잘 안 되는 경우도 많이 볼 수 있습니다. 최고의(!) 파이썬 책으로 유명한 David Beazley가 지난 3월에 PyCon 2008에서 튜토리얼로 시스템 프로그래머를 위한 제너레이터 트릭을 발표했습니다. 여러모로 재미있는 내용인데요~ 저기서는 제너레이터를 주로 다뤘는데, 몇 가지 이터레이터와 관련된 사용 예를 소개해 드리려고요~ (별로 관련 없는 것이 뒤죽박죽;;)

zip을 쓰면 이터레이터를 일정 개수씩 빼오는 걸 간단하게 할 수 있습니다.

>>> s = iter('abcdefghijklmnopqrstvwxyz')
>>> zip(range(5), s)
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
>>> zip(range(5), s)
[(0, 'f'), (1, 'g'), (2, 'h'), (3, 'i'), (4, 'j')]

빈칸이 올 때까지 입력받은 걸 리스트로 받으려면?

>>> list(iter(raw_input, ''))
Python - 
batteries included

['Python - ', 'batteries included']

딕셔너리 2개에서 양쪽 중 하나라도 키가 있는 것들을 뽑아서 돌려서 합치려는 경우..

>>> a = {'a': 3, 'b': 4}
>>> b = {'b': 5, 'c': 6}
>>> set(a) | set(b)
set(['a', 'c', 'b'])
>>> dict((key, a.get(key, 0) + b.get(key, 0)) for key in set(a) | set(b))
{'a': 3, 'c': 6, 'b': 9}

(이 경우는 collections.defaultdict를 써도 간단합니다.)

리스트를 받아서 순위를 계산

>>> from operator import *
>>> s = [9, 8, 1, 5, 3, 7, 4, 0]
>>> map(itemgetter(0), sorted(enumerate(s), key=itemgetter(1), reverse=True))
[0, 1, 5, 3, 6, 4, 2, 7]

(이건 뭐 주제하고 상관도 없고 제멋대로;;;)

댓글 2 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 문법을 내 맘대로 python4ply

그동안 파이썬은 조건 표현과 과 with절같이 문법을 바꿔달라는 수 많은 사용자들의 요청을 받아들여 문법을 추가한 사례가 여럿 있기는 했지만, switch-case나 for-from-to 같이 디자인 상의 일관성 문제로 인해서 받아들여지지 않은 문법도 많이 있었습니다.

요즘 계속 영역-특정적 언어(Domain-specific Language)에 대한 사용처가 늘고 있는데 파이썬을 특정 목적에 쓰자면 종종 대형 프로젝트에서는 문법을 고치는 게 득이 될 경우도 많이 생각할 수 있습니다. 그래서 전처리기를 사용해서 쓰는 방법도 있지만, python4ply는 이걸 좀 더 깔끔한 방법으로 처리하기 위해 등장했습니다. 나온지는 꽤 오래되었지만, 갈 수록 중요한 변환점을 찍은 모듈로 지속적으로 언급되고 있습니다.

원래 PLY는 파이썬에서 lex/yacc를 지원하기 위한 파서 제너레이터인데, python4ply는 PLY에서 파이썬을 파싱하고 그걸로 파이썬 바이트코드를 생성해서 직접 파이썬 코드 파싱/컴파일을 만들었기 때문에 그 부분을 직접 조절할 수 있도록 했습니다.

예를 들면 튜토리얼에서 예제로 들고 있는 것에서 이런 문법도 보여주고 있습니다.

# 2진법으로 끊어쓰기
amount = 0b1_0011_0001_0010_1101_0000_0001
print "You owe me", amount, "dollars"``

# 펄식 정규식 연산자
for line in open("nucleosome.pdb"):
  if line =~ m/(ATOM  |HETATM)/:
      count += 1``

파싱과 컴파일을 모두 건드리고 있으니 문법은 원한다면 얼마든지 바꿀 수 있는데, python4ply는 소스 구조도 아주 깔끔하게 잘 정리되어 있는 편이라, DSL을 만들어야 하는 상황이라면 언제나 유용하게 쓸 수 있을 듯 합니다.

사실 python4ply가 나온 이후로 python-dev 메일링에 누군가 새로운 문법 뭔가 좀 넣어달라, 또는 파이썬 3에 2.x호환성 좀 넣어달라 요청만 올라오면 python4ply URL을 누군가 덥썩 던져주고서는 그 다음부터는 무시하는 분위기가 됐지요. :> 누구나 어렵지 않게 할 수 있을 거라고 생각하고 있던거지만, 실제로 구현이 되고나니 영향을 꽤 많이 미치고 있는게 역시 가능하다고 생각만 하고 있는 것과 실제 구현체가 나온 것은 영향력이 차이가 좀 있군요.

댓글 1 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬을 두루 이해하는 데 도움 되는(?) 퀴즈

얼마 전에 "사랑하지 않으면 떠나라"의 서평 제목으로는 좀 독특한 "당신은 자바 가상 머신을 죽일 수 있나요?"라는 글을 보고 떠올라서, 혹시 파이썬을 쓰다가 발생하는 문제를 얼마나 잽싸게 해결하고, 문제가 생길만한 코드를 짜지 않는 등 이해정도와 관련된 것을 시험해 볼 수 있는 문제를 한 번 내 봤습니다. -ㅇ-;; 물론 제가 냈기 때문에 제가 아는 한에서만 나온거라 좀 편향돼 있을 수도 있지만.. 그냥 재미로 한 번;;

1. 다음 파이썬 타입 중에서 "일반적으로" 큰 수를 다룰 수 있는 순서대로 정렬하세요.

decimal.Decimal   int/long   float (inf 제외)   str (36진법으로 풀어 쓴다)


2. 다음 파이썬 타입 중에서 해시가 불가능한 타입을 모두 고르세요.

unicode  float  set  decimal.Decimal  dict


3. 다음 중 최대 메모리 사용량이 가장 많은 코드를 고르세요.

(ㄱ) max([i for i in range(1000)])
(ㄴ) max([i for i in xrange(1000)])
(ㄷ) max(i for i in range(1000))
(ㄹ) max(i for i in xrange(1000)) 


4. 부모 클래스에서 __x(self) 메쏘드가 있을 때, 자식 클래스에서 부모 클래스가 정의된
소스코드를 수정하지 않고 호출할 수 있는 방법을 가능한 한 다양한 방법으로 쓰세요.


5. pymalloc을 넣고 컴파일했을 때 발생하는 현상이 아닌 것은?

(ㄱ) 할당한 메모리보다 더 많이 할당된다.
(ㄴ) 같은 루틴을 반복할 경우 메모리가 샐 수도 있다.
(ㄷ) 할당된 객체가 쓰는 메모리를 해제해도 남아있을 수도 있다.
(ㄹ) 메모리 할당이 더 느려질 수도 있다.


6. 다음 중 메모리 할당 횟수가 가장 많은 코드는?

(ㄱ) map(int, range(100))
(ㄴ) map(str, range(100))
(ㄷ) map(long, range(100))
(ㄹ) map(unicode, range(100))


7. exec "x ** y" 를 실행 할 때 발생할 수 있는 예외가 가장 아닐 "것 같은" 것은?

NameError OverflowError MemoryError TypeError RuntimeError TabError ZeroDivisionError


8. 모듈을 들여오는 과정과 관련된 모듈 또는 객체가 아닌 것을 고르세요? (파이썬 2.x 기준)

sys.path  zipfile  imp  warnings  marshal  sys.modules  __future__


9. x += x 했을 때 할당된 메모리 크기가 변하지 않을 수도 있는 초기 x 값을 모두 고르세요?

(ㄱ) sys.maxint  (ㄴ) [1, 2]   (ㄷ) 'guido' * 10  (ㄹ) True  (ㅁ) sys.version


10. 다음 중 키보드 입력(ctrl-c)으로 중단할 수 있는 것를 모두 고르세요?

(ㄱ) select.select   (ㄴ) 9 ** 9999  (ㄷ) os.listdir  (ㄹ) Decimal(9) ** 9999  (ㅂ) deque().add


11. CPython VM을 죽일 수 있는 파이썬 코드를 3가지 방법 이상 작성해 보세요.


12. 다음 중 오버라이딩으로 동작을 "바꿀" 수 있는 경우를 모두 고르세요?

(ㄱ) not A  (ㄴ) A is B  (ㄷ) A = B  (ㄹ) A != B  (ㅁ) A <- B  (ㅂ) A and B  (ㅅ) A | B  (ㅈ) (A, B)


13. 상속 받은 자식 클래스에서 단순하게 메쏘드를 오버라이드 했을 경우에도 베이스 클래스에서
"강제로" 자기 메쏘드를 부르게 하고 싶을 때 쓸 수 있는 방법 몇 가지?


14. gc.collect()가 해결할 수 없는 문제는?

(ㄱ) 순환 참조   (ㄴ) C모듈의 전역 변수  (ㄷ) C객체의 멤버 변수  (ㄹ) C객체 간의 순환 참조


15. x += y를 실행했는데 ImportError가 발생했다. 어떤 상황일까? 가설을 5개 이상 생각해 보세요.


16. x = y를 실행했는데 TypeError가 발생했다. 어떤 상황일까? 가설을 2개 세워 보세요.


17. if list(x): raise SystemExit 했더니 프로그램이 종료했다. 어떤 상황일까? 가설을 5개 세워 보세요.


18. def x(a, b):에 대해서 x(1, 2)했는데 TypeError가 난다. 어떤 상황일까? 가설을 5개 세워 보세요.


19. import os; os.listdir('.') 했더니 AttributeError가 난다. 어떤 상황일까? 가설을 3개 세워 보세요.


20. print x하면 0인데 if x: print "Yay!" 하면 Yay!한다. 어떤 상황일까? 가설을 3개 세워 보세요.

댓글 11 개 | 트랙백 0 개 (보낼곳) | 태그 python


구글코리아 테크토크: 파이썬 - 상상력에 날개 달기

어제 구글코리아에서 있었던 테크토크 자료입니다. 주제는 "파이썬 - 상상력에 날개 달기"로 C와 자바를 쓰시던 프로그래머들이 자주 갇히게 되는 상상력의 굴레에서 벗어날 수 있게, "헉 이런 것도 되다니!"를 통해서 창의적인 구조를 디자인하는데 도움을 드리는 것이 목적이었습니다.

소개해드리는 소스코드는 각 문제를 해결하는 데 최적의 해결방법이 아니라, 이렇게 하는 방법도 있다는 정도이니 감안해서 참고하시면 좋겠습니다~ (실제로 여기서 사용된 hack을 그냥 막 도입해서 쓰시면 왕따당합니다. ^^*)

녹화된 현장 비디오는 편집이 완료되는대로 유튜브 한국 사이트에 올리신다고 합니다. ^.^;;;

댓글 4 개 | 트랙백 1 개 (보낼곳) | 태그 python


파이썬 웹 프로그램 순식간에 만들어서 친구들과 나눠 쓰기

얼마 전에 nohmad님 이 소개해 주신 AppJet을 보고 파이썬 모듈들을 쓸 수 있으면 무척 좋겠구나! 생각을 했었는데요. 결국 파이썬으로 된게 나왔습니다.

이름은 UtilityMill 대략 우리말로 대장간 정도 되겠습니다~ Zope에서 입력 변수를 웹 UI에서 편집하면 주석에다 달아주듯이, Utility Mill에서는 주석에 달지는 않지만, 양식에 들어갈 변수들의 성질만 정해주면 자동으로 웹 양식을 만들어줍니다. 파이썬 프로그램에는 그냥 전역변수로 들어오기 때문에 그냥 쓸 수 있고, 출력은 print하면 됩니다. 간단!

저도 간단하게 테스트를 해 보려고 예전에 만들었던 한국어 lorem ipsum의 라틴어-한글 변환기 부분을 집어 넣어 보았습니다. 뭔가 처음엔 이상한 에러가 났었는데, 어딘가 고치다보니 저절로(?) 고쳐져서 잘 돌아가는군요;

그러나, 그냥 단순한 프로그램만 돌리면 아무래도 파이썬의 매력은 다 살리지 못한 것일텐데, 여기서는 파이썬의 표준 라이브러리 "전부"뿐만 아니라, BeautifulSoup, numpy, PyCrypto, feedparser, pyparsing, PyRSS2Gen, ReportLab, SciPy 까지도 지원하기 때문에, 정말 간단한 도구들은 웬만하면 불편없이 만들 수 있군요. 웹을 긁어와서 작업하는 것도 가능하기 때문에, 앞으로 아이디어가 생기면 저기에 올려놓고 모두 같이 쓸 수 있게 해야겠습니다. ^-^*

댓글 1 개 | 트랙백 1 개 (보낼곳) | 태그 python


웹페이지에서 자동으로 RSS 만들기

지난 달에 봤던 뮤지컬 《헤어스프레이》가 너무 좋아서 마지막 회차 공연 때 또 보려고 3차 티켓 오픈 소식을 들으려고 웹페이지를 감시하고 있었습니다. 아 그런데 예약페이지를 Update Scanner로 감시해 놓으니까, 정작 보고싶은 예약가능일 변경은 잘 안 보이고, 사이트 전체 예약순위 같은 것만 보이고 그래서, 아 이럴 때 RSS가 있었으면! 하고 무척 답답했었는데요. 그래서 결국 뭔가 다른 방법이 없을까 찾다가 인터파크 티켓 공지사항에 티켓오픈 며칠 전에 공지가 올라온다는 것을 알고 저걸 변경사항 보려니 그것도 좀 그래서 예전에 까나리님 블로그에서 본 적 있는 RSS 자동 만들어주기 사이트가 생각났습니다.

게시판같은 것을 RSS로 자동으로 만들어 주는 feedity.com을 써 봤는데, 치명적인 문제가 인터파크에서는 공지사항을 <제목> 이렇게 <>로 감싸서 올리는 와중 < >를 그냥 써버려서 태그로 인식되어 feedity에서 다 잘라버리는 것입니다. 그래서 결국 제목에서 중요한 부분만 다 없어지는 효과가;; 그리고 본문 요약이 잘 안 되는지 글 내용도 이렇게 다 빈걸로 올라오고 광고만 떨렁 올라오더군요.

그래서 아 이거 재미있겠다 갑자기 호기심이 생겨서 직접 만들어보자 하고 마음을 먹었습니다. -ㅇ-; 우선 요구사항은

  • <제목> 같이 개떡같이 HTML을 써도 대충 알아듣자
  • 본문 요약을 어떻게든 만들어서 넣자. (요렇게)
  • 레이아웃이 바뀌어도 동작할 수 있게 DOM이나 HTML 레이아웃은 생각하지 말자.

로 정하고 대략적으로 실행 단계는 이렇게 정했습니다. (아마도 대부분의 자동 RSS 사이트들이 이렇게 동작하지 않을까 예상 -ㅇ-)

  • 게시판 목록이 있는 HTML을 들고 와서 링크를 모두 뽑아낸다.
  • 뽑아낸 링크들을 자기들끼리 비교해서 서로 간의 (차이) 거리를 잼. 예를 들어 숫자 1개 차이는 1점, 알파벳 1개 차이는 2점
  • 링크 간의 거리 행렬(distance matrix)을 가지고 계층적 군집화(hierarchical clustering)를 해서 떼로 만듦.
  • 여러 군집 간에서 적당한 기준으로 "게시판 글 목록" 군집을 골라 냄. (여기서는 군집 내 최대 거리, 군집의 크기, 군집 내 링크들의 본문 길이를 기준으로)
  • 골라낸 군집의 링크들의 문서를 모두 가져옴
  • 링크를 2개씩 짝지어서 HTML을 비교해서 차이점이 각 링크의 본문이라고 가정해서 뽑아냄
  • 뽑아낸 본문들과 제목 등을 이용해서 RSS 생성

그렇게 해서 autorss라는 이름으로 하나 만들었습니다. 처음엔 간단할 것 같아서 스크립트 파일 1개로 50줄 안에 끝내야지 했는데, 하다보니 모듈 import가 17개인 거대 스크립트가 돼 버렸네요 --;;

생각보다 잘 돌아네요.. ^.^;; 이제 편하게 티켓 오픈 소식을;;; 다른 RSS 제공해 주지 않는 답답한 사이트를 보고 싶을 때 한번 적용해보세요~ (아직 그렇게 소스가 일반화돼 있지 않아서 좀 고칠 부분이 많을 수도 있습니다. 다른 사이트에는 적용해 보니까 DC인사이드 갤러리에도 그런대로 잘 동작하네요. :)

댓글 14 개 | 트랙백 0 개 (보낼곳) | 태그 happyhacking python


자주 바뀌는 흩어져 있는 여러 변수를 다루는 여러 방법

파이썬 마을에 올라온 질문을 읽다가 답변을 하려고보니, 파이썬을 처음 접하는 분들이 빈번하게 궁금해 하시는 문제인 것 같아서, 좀 더 깊게 후벼파서 글로 만들어 봅니다.

질문의 요지는 아래 소스에서 a~f까지 딕셔너리를 임의로 섞고 싶은데 목록이 많을 수도 있고 자주 바뀌기도 하니까 좀 더 좋은 방법이 없을까 하는 것입니다.

import random

a = {'aa': 1}
b = {'bb': 2}
c = {'cc': 3}
d = {'dd': 4}
e = {'ee': 5}
f = {'ff': 6}

this = [a, b, c, d, e, f]
random.shuffle(this)
print this

이거랑 완전히 똑같은 상황이 자주 일어나지는 않겠지만, 그래도 종종 처음엔 네임스페이스에 변수를 왕창 넣어놓고 이걸 어떻게 해보고 싶은 경우가 있긴 하니까 그래도 파이썬 프로그램들이 자주 사용하는 패턴들로 해결해 보겠습니다.

[1] 정면승부! 네임스페이스 직접 접근해서 거시기하기

import random
import inspect

def shufflelocaldicts():
    outerframe = inspect.getouterframes(inspect.currentframe())[1][0]

    names, objs = [], []
    for name, obj in outerframe.f_locals.iteritems():
        if isinstance(obj, dict):
            names.append(name)
            objs.append(obj)

    random.shuffle(objs)
    for name, obj in zip(names, objs):
        outerframe.f_locals[name] = obj

a = {'aa': 1}
b = {'bb': 2}
c = {'cc': 3}

print a, b, c
shufflelocaldicts()
print a, b, c
shufflelocaldicts()
print a, b, c

첫 번째로는 원래 프로그램 자체는 전혀 안 건드리고 그냥 원하는 작업만 하는 방법입니다. (변수 개수는 소스코드 길이를 줄이려고 3개로 줄였습니다.) shufflelocaldicts()를 호출하면 바깥 프레임에 접근해서 바깥 네임스페이스의 딕셔너리를 모두 가져다가 섞은 다음에 다시 넣어줍니다. 이건 고칠 부분이 줄기는 하지만, 모든 하이테크 방법들이 그렇듯 깨지기도 쉽고 사용하는 사람들이 제대로 알지 않으면 엉뚱하게 흘러갈 수도 있는 잠재적 위험성이 있습니다.

[2] 구차하더라도 열심히 고치기

import random

shufflers = []
_ = shufflers.append

a = {'aa': 1}; _('a')
b = {'bb': 2}; _('b')
c = {'cc': 3}; _('c')

def shuffledicts():
    shuffled = shufflers[:]
    random.shuffle(shuffled)

    gdict = globals()
    values = [gdict[k] for k in shufflers]
    for newname, obj in zip(shuffled, values):
        gdict[newname] = obj

print a, b, c
shuffledicts()
print a, b, c
shuffledicts()
print a, b, c

두 번째 방법으로 바깥 네임스페이스를 검색하는 흑마법을 쓰지 않고 일일이 등록해주는 것이 있습니다. 이 방법은 일일이 등록해야한다는 것이 약간 중복의 냄새도 있긴 하지만, 단순하고 잘 작동하고 명시적이라서 잘 보인다는 점에서 상당히 자주 사용되는 패턴입니다. 그렇지만 역시 이 경우에는 그다지 썩 좋지는 않네요.

[3] 좀 머리를 굴려보자

import random
import inspect

a = {'aa': 1} # !SHUFFLE!
b = {'bb': 2} # !SHUFFLE!
c = {'cc': 3} # !SHUFFLE!

shufflers = [] # !SHUFFLELIST!

def generate():
    import re, StringIO

    pat_entry = re.compile(r'^([A-Za-z0-9_]+).*#.*!SHUFFLE!$')
    pat_slist = re.compile(r'^([^[]+)\[[^]]*\](.*#.*!SHUFFLELIST!)$')

    srcout = StringIO.StringIO()
    names = []
    for line in open(__file__):
        entry = pat_entry.match(line)
        if entry:
            names.append(entry.groups()[0])

        slist = pat_slist.match(line)
        if slist:
            head, tail = slist.groups()
            print >> srcout, '%s[%s]%s' % (head, ', '.join(map(repr, names)), tail)
        else:
            srcout.write(line)

    open(__file__, 'w').write(srcout.getvalue())

def shuffledicts():
    shuffled = shufflers[:]
    random.shuffle(shuffled)

    gdict = globals()
    values = [gdict[k] for k in shufflers]
    for newname, obj in zip(shuffled, values):
        gdict[newname] = obj

def run():
    print a, b, c
    shuffledicts()
    print a, b, c
    shuffledicts()
    print a, b, c

if __name__ == '__main__':
    print "Regenerating the source code.."
    generate()

이번에는 노장 프로그래머들이 동적 언어들이 그렇게 활발하기 전에 예전에 자주 사용했던 방법으로, 태그를 보고 코드를 자동생성하는 방법입니다. 요새도 전혀 사용되지 않는 것은 아닌데, 프로그램이 변경될 때마다 스크립트를 한 번씩 실행해 주면 자동으로 소스의 주석을 보고 [] 사이의 빈칸을 채워줍니다. 이 기법은 파이썬 프로그램보다는 C프로그램에서 보통 유용하게 쓰이는데요, cog같은 툴들을 쓰면 좀 더 간편하게 할 수 있습니다. 이건 뭐 대체로 잘 작동하기는 하지만, 어쩌다 소스코드 업데이트가 빠지면 재앙이 생기기도 하고, 그다지 멋지지 않다는게 좀 흠입니다. 주석에 의존한다는게 특히 좀 찝찝하죠. -o-

[4] 객체지향 좀 배운 사람인 척 티내보기

import random

class ListItemProxy(object):
    def __init__(self, obj, idx):
        self.__obj = obj
        self.__idx = idx

    def __getattr__(self, name):
        if name.startswith('__'):
            return self.__dict__[name]
        else:
            return getattr(self.__getobj__(), name)

    def __repr__(self):
        return repr(self.__getobj__())

    def __getobj__(self):
        return self.__obj[self.__idx]

    def __setitem__(self, name, value):
        return self.__getobj__().__setitem__(name, value)

    def __getitem__(self, name):
        return self.__getobj__().__getitem__(name)

    def __detitem__(self, name):
        return self.__getobj__().__detitem__(name)
        

deck = [{'aa': 1}, {'bb': 2}, {'cc': 3}]
a = ListItemProxy(deck, 0)
b = ListItemProxy(deck, 1)
c = ListItemProxy(deck, 2)

print a, b, c
random.shuffle(deck)
print a, b, c
random.shuffle(deck)
print a, b, c

앞에서 썼던 방법들은 직관적이고 단순하기는 해도 역시 너무 무식한 방법같은 냄새가 납니다. 이번엔 파이썬에서 객체 꽁수를 배우고 한참 기분낼 때 아주 재미있게 하는 객체 오버라이딩으로 위임(proxy) 객체 만들어주기입니다. 대형 프로젝트에서는 이런 스타일을 종종 사용하기도 하지만, 짧은 스크립트에서 쓰면 좀 격에 안 맞기는 하죠. 직접 딕셔너리를 만들어서 그걸 어쩌고 저쩌고하는 대신에 리스트에 원래 걸 넣어두고 그 첫번째, 두번째~ 등을 간접적으로 작업을 지원해 주도록 하는 방법입니다. 이 방법은 shuffle이 매우 빠른 게 장점이고, 참조가 리스트 하나 안에 모여서 깔끔하게 정리가 돼 있는게 좋습니다. 그런데 문제는 역시 위임이 중간에 거치기 때문에 속도상 불이익이 있고, 위임을 제대로 하려면 위 소스보다 훨씬 길게 짜야 한다는 거겠죠.

[5] 딕셔너리들을 모두 관리하는 오버마인드

import random

class sdict(dict):
    alldicts = []

    def __init__(self, *args, **kwds):
        dict.__init__(self, *args, **kwds)
        self.alldicts.append(self)

    @classmethod
    def shuffle(klass):
        dicts = []
        for d in klass.alldicts:
            dicts.append({})
            dicts[-1].update(d)
            d.clear()

        random.shuffle(dicts)
        for d, o in zip(klass.alldicts, dicts):
            d.update(o)


a = sdict({'aa': 1})
b = sdict({'bb': 2})
c = sdict({'cc': 3})

print a, b, c
sdict.shuffle()
print a, b, c
c['rr'] = 9
sdict.shuffle()
print a, b, c

이번엔 딕셔너리 자체를 상속받아서, 2번에서처럼 따로 등록하는 대신 딕셔너리 초기화 도중에 자동으로 등록되게 하고, 4번처럼 위임을 구질구질하게 하지 않고도 자체가 그냥 딕셔너리가 되게 했습니다. 요것도 어느 정도 장점이 있긴 하지만, 역시 딕셔너리를 만들 때 마다 일일이 캐스팅을 해 줘야하고, 원래 객체의 참조를 건드리려면 역시 이름 없이는 할 수 없기 때문에, 참조를 놔둔채로 내용을 바꾸기 위해서 내용을 싹 비워서 다른데 옮겼다가 다시 부어주는 좀 심각하게 비싼 작업이 들어가는게 단점입니다.

[6] 억지로 구겨넣지 말고 원래 모양을 바꿔보자

import random

class ShuffleDeck(object):
    def shuffle(self):
        names = self.__dict__.keys()
        random.shuffle(names)
        for k, v in zip(names, self.__dict__.values()):
            self.__dict__[k] = v

deck = ShuffleDeck()
deck.a = {'aa': 1}
deck.b = {'bb': 2}
deck.c = {'cc': 3}

print deck.a, deck.b, deck.c
deck.shuffle()
print deck.a, deck.b, deck.c
deck.shuffle()
print deck.a, deck.b, deck.c

앞에서 썼던 여러 구질구질한 방법들은 결국 네임스페이스에 흩어져 있는 것들을 어떻게든 관리를 하려다보니 생기는 문제입니다. 그냥 네임스페이스에 흩어지지 않게 따로 모아두면 결국 그렇게 고생 안 해도 간단하게 해결됩니다. 굳이 __dict__를 쓰지 않더라도 그냥 리스트에 번호붙여서 넣어서 쓰거나, 다양한 깔끔한 방법이 네임스페이스에 흩어놓는 것만 포기하면 마구 생겨납니다.

결국은!

이 경우에 우선 문제를 해결하려다보면 앞에서 소개해 드렸던 다양한 방법으로 억지로 해결할 수도 있습니다. 다 만들어 놓은 프로그램을 막판에 약간 어떻게든 고쳐서 출시를 해야하는 경우나, 당장 여자친구가 얼른 안 오면 삐진다고 문자를 수십통을 보내고 있는데 과장님은 얼른 고쳐내라고 옆에서 1분마다 보채고 있을 때 뭐 이런 극한 상황에서나 얼른 쓰고 도망갈 때 쓸만한 방법이겠죠;

역시 이 문제의 경우에는 질문 자체의 요구사항을 파이썬에서 쉽게 해결할 수 있는 방법으로 바꾸는 게 중요합니다. 딕셔너리 여러개를 마구 섞는 문제라고 최종 문제를 보면 상상력의 한계가 있지만, 그것보다 원초적으로 다뤄야하는 문제를 보면 딕셔너리를 여러개 마구 섞는게 아니라 간단하게 해결할 수 있는 다른 자료구조가 반드시 있을 것 같은 예감이 강렬하게 드는군요. -O-; (그리고 마지막으로 하나 덧붙이고 싶은 것은, 생각보다 원래 질문의 코드도 보기에는 좀 그렇더라도 복잡한 방법을 안 썼다는 점에서 그런대로 쓸만한 코드입니다. 물론 문제 자체를 바꾸는게 더 좋을 것 같긴 하지만요;)

댓글 3 개 | 트랙백 0 개 (보낼곳) | 태그 python


고등학생을 위한 Summer of Code, GHOP Contest

Summer of Code를 여러 해 동안 성공적으로 운용해오고 있는 구글에서 드디어 얼마 전, 고등학생을 위한 행사를 공개했습니다. 이름은 The Google Highly Open Participation Contest이고, 대상은 13세 이상이고 대학이전의 학교(한국에서는 초,중,고등학교)에 다니고 있는 사람입니다. 그리고, 북한에 사는 학생들은 참가가 제한됩니다. -O- 그동안 SoC가 대학생이 주 대상이었는데, 실제로 요즘은 고등학생들이 꽤 많이 참가하고 있고, 앞으로 오픈소스 새싹을 키우려면 (상투적이지만;;) 중,고등학생이 프로젝트에 적극적으로 참여할 수 있는 이런 기회가 생겨서 무척 좋습니다.

파이썬 재단의 GHOP 목록을 보면 그다지 어렵지 않으면서도 파이썬의 중요한 부분에 재미있게 참여할 수 있는 여러 작업들을 가지고 있어서 중고등학생이 오픈소스 프로젝트의 마수에 걸려들게 하는 좋은 미끼일 것 같군요. :-)

구체적인 형식은 Summer of Code와는 많이 다른데요, 큰 프로젝트를 멘터와 함께 만들어서 제안하는 방식이 아니라, 일정기간 (2007년 11월 27일부터 2008년 2월 3일까지)동안 이뤄낸 작은 작업들에 대해 보상을 받습니다. 쿠폰바꾸는 것과 비슷한데요, 도장 1개는 24달러짜리 티셔츠를 받을 수 있고, 3개는 100달러를 현금으로 받을 수 있습니다. 1인당 티셔츠 1개 + 500달러 현금으로 최대 상금이 제한되어있습니다.

구체적인 규칙은 GHOP 2007-8 Rules에 나와 있습니다. 우리나라의 열의 넘치는 중고등학생들이 많이 참가해서 다음 행사에 티셔츠 우루루 입고 참가하면 좋겠네요~ :)

댓글 3 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 모듈 관광 4회: 기본제공 자료구조 겉핥기!

오늘은 파이썬에서 기본으로 제공하는 자료구조가 어떤 것이 있는지 겉핥기식으로 쭉 고속으로 관광가겠습니다~ :)

내장 자료구조

우선 기본 내장자료구조 str, unicode, list, tuple, dict, set 여섯가지에서 아무 기능이나 하나씩 써 봅시다. ^.^;

>>> '6,7-dihydro-1-methyl-7-oxo-3-propyl'.partition('-')
('6,7', '-', 'dihydro-1-methyl-7-oxo-3-propyl')

>>> u'def\ttest()'.expandtabs()   # unicode 타입
u'def     test()'

>>> l = [1, 2, 3, 4]   # list 타입
>>> l.sort(key=lambda v: - v**2 % 5)
>>> l
[2, 3, 1, 4]

>>> t = 3, 4    # tuple 타입
>>> t * 5
(3, 4, 3, 4, 3, 4, 3, 4, 3, 4)

>>> d = dict.fromkeys('12345')
>>> d.update({'4': 1, '6': 2})
>>> d
{'1': None, '3': None, '2': None, '5': None, '4': 1, '6': 2}

>>> set('TOMMARVOLORIDDLE') - set('IAMLORDVOLDEMORT')
set([])

밑의 타입들은 보통 다른 언어에서는 자료구조라기보다는 그냥 primitive 타입이지만 그래도 그냥 나머지도 끼워줘 봅시다;

>>> map((5).__mul__, range(3))  # int/long 타입
[0, 5, 10]

>>> (1+5j)**2           # complex 타입
(-24+10j)

>>> divmod(5.3, 3.1)    # float 타입
(1.0, 2.1999999999999997)
>>> bf = buffer('ARNDCQEGHILKMFPSTWYVBZX', 5, 500)
>>> len(bf), bf[:3]     # buffer 타입 (사실은 C 모듈에 붙어야 유용..)
(18, 'QEG')

>>> s = slice(1, 10, 2)
>>> 'ARNDCQEGHILKMFPSTWYVBZX'[s]
'RDQGI'

bool, frozenset 등등은 그냥 넘어갑시다 -ㅇ-;

이제 기본 자료형을 보았으니 다음으로 표준 확장 모듈 자료구조들을..

표준 라이브러리 자료구조

작은 값을 많이 저장할 때 효율적으로 메모리또는 디스크에 들고 있을 수 있는 array 모듈입니다.

>>> import array
>>> arr = array.array('h')
>>> arr.append(123)
>>> arr.extend([34,5,8])
>>> arr.tostring()
'{\x00"\x00\x05\x00\x08\x00'
>>> arr.tofile(open('myarr', 'w'))
>>> arr.pop()
8

heapq 모듈은 기본 list자료형을 가지고 우선순위 큐(priority queue)로 쓸 수 있게 해줍니다. 스케줄링 루틴 같은 곳에서 정말 편리합니다. 속도가 무진장 빠릅니다. :)

>>> import heapq
>>> l = list(['ham', 'egg', 'spam', 'britney', 'guido'])
>>> heapq.heapify(l)
>>> heapq.nlargest(1, l)
['spam']
>>> heapq.nsmallest(2, l)
['britney', 'egg']
>>> map(heapq.heappop, [l]*5)
['britney', 'egg', 'guido', 'ham', 'spam']

bisect 모듈은 정렬된 list에서 이진검색법으로 자료의 위치를 찾아줍니다. dict로 다루기에는 자료의 양이 많거나, 순서가 중요한 경우에 쓸만합니다. 아래 예제는 동전을 몇 번 던지면 앞면이 나오나 같은 경우를 나타내는 기하분포 랜덤함수를 bisect로 만든 방법인데, 그냥 계산해버리는 방법도 있지면 예제를 위해서 --; (실제로 분포함수가 없고 그냥 이산확률만 덩그러니 주어졌을 때 bisect가 꽤 쓸만합니다.)

>>> import bisect, random
>>> geometric_cdf = lambda p, k: 1 - (1 - p)**k
>>> cumprob = map(geometric_cdf, [0.5] * 20, range(20))
>>> cumprob[:10]
[0.0, 0.5, 0.75, 0.875, 0.9375, 0.96875, 0.984375, 0.9921875, 0.99609375, 0.998046875]
>>> georand = lambda: bisect.bisect_left(cumprob, random.random())
>>> [georand() for i in range(15)]
[1, 1, 1, 4, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 1]

다음은 어정쩡한 이름을 달고 있는 collections 입니다.

>>> import collections
>>> counter = collections.defaultdict(lambda: 0)
>>> for c in 'wii tennis':
...     counter[c] += 1
... 
>>> counter.items()
[(' ', 1), ('e', 1), ('i', 3), ('n', 2), ('s', 1), ('t', 1), ('w', 1)]

>>> dq = collections.deque()
>>> dq.append(1); dq.append(2)
>>> dq.appendleft(3)
>>> dq
deque([3, 1, 2])
>>> dq.pop()
2
>>> dq.popleft()
3
>>> dq
deque([1])

빼놓으면 서운하니까 bsddb 도 한 번..

>>> import bsddb
>>> hdb = bsddb.hashopen('test', 'c')
>>> hdb['byam'] = 'uhung`'
>>> hdb.keys()
['byam']
>>> del hdb['byam']
>>> hdb.close()

>>> rndb = bsddb.rnopen('test2', 'c')
>>> rndb[1] = 'sujiniya'
>>> rndb[3] = 'geosigi'
>>> rndb.items()
[(1, 'sujiniya'), (3, 'geosigi')]
>>> rndb[2] = 'wae?'
>>> rndb.items()
[(1, 'sujiniya'), (2, 'wae?'), (3, 'geosigi')]

>>> # B+Tree는 너무 똑같은 것 계속하면 지루해서 생략 -ㅇ-;

이 외에도 UserString, UserDict, StringIO, decimal, Queue 등 유용한 것이 많이 있습니다만, 신비감 조성을 위해서 모두 소개하지 않고 이쯤에서 마칩니다. =3=33

댓글 2 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 모듈 관광 3회: durus

별로 관심있는 분들이 없는 것 같아서 다음 편을 안 쓰고 있었는데, 오늘 어딘가에서 모듈 관광을 읽었다는 분을 만나고 어딘가에는 읽는 분이 있구나! 싶어서 생각난 김에 한 번 또 씁니다;

오늘의 모듈은 durus 입니다. 이미 SCGI같은 여러 프로그램들로 유명한 CNRI MEMS-Exchange 팀에서 만들었습니다. 원래 파이썬 뿐만 아니라 다양한 환경에서 캐시 공유나 분산 배치 작업 같이 한 데이터 공간을 여러 프로세스가 써야하는 경우에는 memcached가 거의 압도적으로 많이 쓰이는 솔루션이겠지요. durus는 이와 비슷한 역할과 표준 shelve의 기능이 모두 필요한 경우에 요긴하게 쓸 수 있습니다.

미리 서버를 띄워두고.

cysteine(perky):~% durus -s
Storage file=/tmp/tmpzRHYiQ address=127.0.0.1:2972
Ready on 127.0.0.1:2972 with 0 objects

시험삼아 접속해서 UUID 객체를 하나 넣어봅니다.

cysteine(perky):~% python
Python 2.5.1 (r251:54863, Nov  5 2007, 09:29:15) 
[GCC 4.2.1 20070719  [FreeBSD]] on freebsd7
Type "help", "copyright", "credits" or "license" for more information.
>>> from durus.client_storage import ClientStorage
>>> from durus.connection import Connection
>>> c = Connection(ClientStorage())
>>> 
>>> import uuid
>>> c.root['uuid'] = uuid.uuid1()
>>> c.commit()
>>> 

이렇게 되면 서버에 커밋되었다는 메시지가 보입니다.

Committed   1 objects 147 bytes at 2007-11-26 20:03:52.203449

이제 다시 클라이언트를 따로 띄워서 접속해서 확인해 봅니다.

cysteine(perky):~% python
Python 2.5.1 (r251:54863, Nov  5 2007, 09:29:15) 
[GCC 4.2.1 20070719  [FreeBSD]] on freebsd7
Type "help", "copyright", "credits" or "license" for more information.
>>> from durus.client_storage import ClientStorage
>>> from durus.connection import Connection
>>> c = Connection(ClientStorage())
>>> c.root['uuid']
UUID('4353180c-9c0f-11dc-bffd-0001028cad3c')

역시 MEMS Exchange에서 만들었던 다른 작품들과 같이 기능도 괜찮으면서 단순해서 속도도 쓸만합니다. 여러 프로세스가 협업하거나 임시로 떴다 죽었다 하는 프로그램들에서 객체를 다뤄야할 때 쓰면 좋겠네요. :)

댓글 4 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 모듈 관광 2회: imputil

지난 번 모듈 관광 1회의 뜨거운 성원에 힘입어 (;;) 2회를 준비했습니다. 으흐흣~ 오늘은 역시 초기이니, 많이 안 알려진 것을 일부러 골라 봤는데요. 파이썬 표준라이브러리에 들어있지만 문서화가 안 되어 있고 책에서도 좀처럼 소개해주지 않기 때문에, 쓰는 곳도 찾기 힘든 그런 모듈로 이름은 imputil 입니다.

파이썬에서는 임포트 과정을 직접 조작할 수 있는데, 그 기능을 이용한 것이 대표적인게 바로 zipimport 모듈입니다. 그 덕분에 파이썬에서는 모듈을 zip에 묶어놔도 읽어들일 수 있게 되어 있는데요. 이 과정을 직접 만들려면 파이썬 내부 동작을 건드릴 부분이 많다보니 귀찮은 과정을 겪어야합니다. 이런 과정을 파이썬 언어스럽게 간단하게 해결할 수 있게 대충 껍데기를 만들어 놓은 것이 바로 imputil입니다.

관광에서는 역시 복잡한 걸 해 보면 안 되니까, 간단하게 파이썬 프로그램 확장자를 .py가 아니라 .pl인 것도 읽을 수 있게 고쳐봅시다. (-O-;;) 우선 foo.pl 라는 이름으로 파일을 만듭니다.

% cat >foo.pl
print "Hello!"
%

그리고 이제 imputil로 쓱싹~

>>> import imputil
>>> mng = imputil.ImportManager()
>>> mng.add_suffix('.pl', imputil.py_suffix_importer)
>>> mng.install()
>>> import foo
Hello!

플러그인에 괜히 자기 프로그램 이름을 붙여서 파이썬 아니고 뭔가 특별한 것인 척 할 때 좋겠죠? :) 괜히 암호화해 놨다가 풀면서 임포트하게 할 수도 있고.. 여러가지 응용이 가능하겠습니다. --- (플러그인 프레임워크를 위해 제어할 때는 마찬가지로 표준 모듈인 imp를 쓰면 멋있게 만들 수 있습니다.)

댓글 1 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 모듈 관광 1회: ezpyinline

요새 블로그가 있다는 존재를 스스로 까먹을 정도로 안 썼더니만 뭔가 블로그 들어오기가 무서워서, 연재물을 하나 구상했습니다. 예전에 회사에서 한번 옆 사람한테 "모듈 관광"을 한번 시켜줬더니 좋아하길래 블로그 버전으로 모듈의 용도와 특징만 겉핥아보는 모듈관광 연재를 해 보려구요!

첫 관광명소(?)로 뭐가 좋을까 한참 고민을 하다가, 아무래도 첫회이니만큼 너무 잘 알려진 모듈을 하면 보는 분들이 지루하지 않을까 싶은 생각이 들었습니다. 그래서 결국 아무도 모를만한(;;) 모듈을 찾아봤는데 뭐 사실 인터넷에 다 있는데 아무도 모를리야 없을테고 그래도 잘 안 쓰지만 저는 매일매일 쓰는 모듈을 하나 건져 올려 봤습니다. :)

처음으로 소개해 드릴 모듈은 "ezpyinline"이라는 모듈인데, 이름에서 풍기듯 "쉽게 파이썬에 C를 인라인"할 수 있는 모듈입니다. 이런 건 전에도 몇 번 나오기는 했지만, 그래도 보통 시대 흐름에 따라서 요구사항이 달라지는데 오래 전에 나온 것은 스타일도 다르고 여러모로 취향이 다르니까 새것이 유지만 잘 되면 쓰기는 편할 것 같네요.

인라인 같은 요구사항이 있을 때는 보통 C로 간단하게 인터페이스를 줄인 다음에 pyrex를 쓰거나, SWIG같은 래퍼를 쓰거나, 아니면 직접 다 모듈을 짜버리는 방법이 정통적인 방법입니다. 간단한 것 할 때는 뭘 해도 상당히 귀찮죠. 파이썬은 역시 기동성이 힘! ez붙은 건 거짓말만 아니라면 다 한번씩은 봐 둘 가치가 있겠죠;

관광에 설명이 너무 많으면 안 되니 경치구경으로 대신합니다. :)

>>> import ezpyinline
>>> cfactorial = ezpyinline.C(r"""\
...   int factorial(int v)
...   {
...     int i, r = 1;
...     for (i = 2; i <= v; i ++) r *= i;
...     return r;
...   }""").factorial
>>> cfactorial(1)
1
>>> cfactorial(12)
479001600

진정 보람을 느끼려면 항상 예전과 비교를.. ^.^

>>> def pyfactorial(v):
...     return reduce(lambda x, y: x * y, range(2, v+1), 1)
... 
>>> def mytimeit(f, *args):
...     st = time.time()
...     for i in xrange(1000000):
...             f(*args)
...     return time.time() - st
... 
>>> mytimeit(cfactorial, 12)
0.39980006218
>>> mytimeit(pyfactorial, 12)
5.71780800819

대략 10배 넘게 성능 향상이 있었습니다. 와우~! (뻔한 일에도 감동을;)

그래도 아무래도 타입이 조금만 복잡해져도 자동 처리가 난감해지고 하는 면이 있기 때문에, 아주 인터페이스를 단순화해서 임시 실험용으로 쓸 코드에서 C코드로 가속을 하고 싶을 때 유용할듯 합니다.

자 그럼 다음 이 시간에 또 만나요~ (TV 세계관광 프로그램 컨셉으로;;)

댓글 5 개 | 트랙백 0 개 (보낼곳) | 태그 python


바쁘게 돌아가는 파이썬 3000 요새 소식

파이썬계에서 8월은 파이썬 3000으로 열기가 가득한 한 달이었습니다. 8월 초에 드디어 문자열의 유니코드화 작업이 완료돼서 py3k 브랜치로 머지되었고, 새 I/O 스택도 거의 제 모습을 갖췄습니다. 그리고, 약속을 지키기 위해 드디어 8월 31일(미국 서부시간 기준), 파이썬 3.0a1이 릴리스될 예정입니다.

최근의 변화를 보자면, 문자열 유니코드화는 이미 다른 곳에서 많이 보셔서 지겨울 정도이고, 최근 논란이 많았던 str.format이 얼마 전에 구현이 완료되어서 들어갔습니다. str.format이 그동안 파이썬의 마스코트(?)같았던 존재인 문자열 % 연산자를 대체할 예정입니다.

>>> print('{1} {0}마리가 지지배배'.format(5, '오리'))
오리 5마리가 지지배배
>>> print('{이름}가 {시각}시부터 짹짹'.format(이름='거위', 시각=6))
거위가 6시부터 짹짹

그리고, 아시아 컴퓨터교육계의 염원(?)이었던 국제화 식별자(i18n identifier)가 드디어 들어갔습니다. (여기에 대해서 OLPC 쪽에서도 관심을 보이고 있더군요.)

>>> 출력, 범위 = print, range
>>> 오리, 거위 = 범위(2)
>>> 연못 = [오리, 거위]
>>> 출력(연못)
[0, 1]

그렇지만 아직 토크나이저의 유니코드 처리가 좀 미숙해서 아주 특별한 조건에서만 제대로 동작하고 있는데, 3.0a2 전까지는 어디서든 유연하게 돌아가도록 고치려고 하고 있습니다.

그리고, 전체 디폴트 인코딩 뿐만 아니라, 소스코드 인코딩도 UTF-8이 디폴트로 바뀌었기 때문에, 소스코드를 UTF-8로 작성하면 따로 # coding: 을 쓸 필요가 없어지고, 요새 나오는 언어들하고 비슷하게 가고 있습니다. :)

파이썬 3000에 대해서는 전에 쓴 적이 있기 때문에, 구체적으로 다시는 안 쓰고 조만간 파이썬 3.0a1 출시 기념으로 스크린캐스트로 파이썬 3.0을 소개해 드리는 기회를 한번 만들어볼까 합니다. ㅎㅎ; ^_^

댓글 4 개 | 트랙백 0 개 (보낼곳) | 태그 python


파이썬 합성 프로그래밍 환경

오늘은 요새 여러 블로그에서 인기를 끌고 있는 파이썬 합성 프로그래밍 환경(Synthetic Programming Environment for Python) 에 대해 간단한 리뷰를 해 봅니다.

그동안 파이썬에서 행렬 계산은 Numeric/NumPy, 기타 과학 연산은 SciPy 등 속도가 필요한 분야에 대한 해결책을 대체로 제시하고 있었지만 동적계획법(Dynamic Programming)이나 몬테카를로 시뮬레이션 같이 비교, 분기가 대량으로 발생하는 경우 파이썬만 가지고는 도저히 어떻게 해 볼 수 없는 한계가 어느 정도 있었습니다. Pyrex는 뭔가 좀 답답하고 Psyco는 썩 만족스러운 단계까지 올라가지도 않고, 결국에 눈물을 머금고 C로 주요 코드를 짤 수 밖에 없었습니다.

그래서 몇 군데에서 시도했던 것이 파이썬에서 직접 어셈블리 코드를 실행시간에 생성해서 실행할 수 있는 JIT 어셈블러들이 등장했었는데요. 파이썬 코드랑 어셈블리랑 어색하게 결합하고 있다보니 인라인 어셈블리쓴 C보다 훨씬 더 어색해서 별로 쓰기가 유쾌하지는 않았지요.

합성 프로그래밍 환경(SPE)는 이걸 좀 더 우아하게 해결해보려는 라이브러리 입니다. OpenSSL에서 사용하는 perlasm같이 스크립트 언어의 자체 문법으로 약간은 추상화된 방법으로 명령을 작성하면, 그 코드가 실행되면서 어셈블리 코드가 생성되고 그게 결국 실행되는 방법입니다. 너무 추상화하면 C나 다름 없으니, 거의 1:1로 맞춰볼 수 있을 정도로 간단하게 처리했지만, 사실 "합성(synthetic)"이란 말에서 드러나듯, 파이썬 문법에 대체적으로 끼워넣어서 재사용성을 매우 높일 수 있다는 점이 장점입니다.

간단한 perlasm에서도 풀어쓰기(unroll)이나 스택 셋업, 인수 대량 전달, 디버그 출력, 클린업 같이 많은 작업들을 라이브러리화 해서 쓰고 있어서, 실제 생성되는 최종 어셈블리 코드보다 훨씬 사람이 보기 쉽고 재사용할 수 있는 코드를 씁니다. SPE에서는 perlasm에서 쓰는 것 뿐만 아니라 벡터연산을 훨씬 쉽고 간단하게 쓸 수 있게 해 주기도 하고, 간단하고 단순노동적인 조건문이나 오프셋 계산 같은 것들을 우아하고 보기 쉽게 쓸 수 있도록 해 줍니다. 게다가 파이썬 코드 안에서 섞어 쓸 수 있기 때문에, 귀찮게 왔다갔다 할 필요도 없습니다.

결국, SPE의 모토는 "Skipping C"라고 하는데, 두 가지 의미가 있습니다. 하나는 원래 파이썬이 C언어로 된 인터프리터, 익스텐션 라이브러리, 컴파일러 등을 통해서 기계어로 실행(+번역)되어 왔는데, 이를 건너뛰어서 그냥 파이썬에서 바로 기계어를 쓰자는 의미로 볼 수 있고, 나머지 하나는 개발할 때 C언어로 개발할 필요를 많이 줄여서 C는 쓰지 말자 뭐 이런 의미로 볼 수도 있겠죠. 즉, 평소같으면 C로 상당수 기반을 만들어서 파이썬에서 사용하는 방식을 채택할 경우라도 파이썬과 SPE에서 제공하는 어셈블리를 쓰면 C 안 쓰고도 비슷하거나 빠른 속도를 낼 수 있다는 얘기입니다.

많은 분들이 이쯤 되면, 속도가 상당히 궁금하실텐데, 간단하게 1000만까지 숫자를 더하는 프로그램을 작성했을 때, 파이썬으로 작성된 코드가 3.64초, 내부 코드 및 자료구조가 모두 C로 작성된 NumericPython으로 0.054초, SPE로 0.019초라고 합니다. (SIMD는 사용하지 않음) 요새 C 컴파일러가 좋긴 하지만, 사람은 알고리즘까지도 변경하면서 최적화할 수 있고, 굳이 프로파일링 하지 않고도 어느 정도까지는 분기 수요를 예측할 수 있다는 점 때문에 아무래도 같은 시간 노력하면 짧은 코드는 SPE로 하는 어셈블리가 C보다 이렇게 빠를 수 있지 않을까 싶군요. (물론, 요새 유행하는 프로파일링 컴파일러를 빡시게 돌리면 C나 SPE나 결국 비슷한 최고속도에 도달하겠지만요.)

SPE가 주 대상으로 하는 것은 아무래도 과학계산과 멀티미디어 연산이 되겠지만, 사실 과학연산에서 어셈블리를 다룰 정도로 컴퓨터 구조에 대한 지식이 있는 연구자의 수가 그렇게 많지 않은 것을 생각해 보면 기계 지식없이도 어셈블리를 쉽게 할 수 있게 라이브러리가 많이 확충되어서 "합성"만 해도 프로그램이 되면 아주 좋겠네요. 물론, 파