레이블이 Design Pattern인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Design Pattern인 게시물을 표시합니다. 모든 게시물 표시

20130818

Python Design Pattern

여기에 교훈이 있거나 쓸모가 있는 파이썬 조각코드를 모아놓고 있다. 서브페이지에 가면 파이썬에서 JPype-사용 해크를 이용하여 Weka의 자바 클래스에 접근하는 법을 볼 수 있다.
또, 흥미로운 링크들을 공유하고 싶다:
  • 파일이나 디렉토리 작업을 할 때 (os.path를 사용하여), path 파이썬 모듈을 살펴보면 더 삶을 편하게 살 수 있다. 아주 간단하지만 놀랍도록 유용하다!
  • 또다른 훌륭한 파이썬 모듈은 BeautifulSoup이다. 이 모듈은 실제-세계의 HTML (또는 XML) 파일을 해석하는데 이용할 수 있다. 아주 간단한 API를 제공하므로 결과로 나온 해석 트리를 손쉽게 순회할 수 있다. 그 주요 특징은 HTML을 준수하지 않는 코드도 (가능하면) 막힘없이 시원하게 처리해 준다는 것이다.
  • 정규 표현식이 지원하는 것보다 더 복잡한 것을 해석하고 싶다면 (즉, 정규 언어를 넘어서는 것), 예를 들어 내포된 괄호나 적절하게 인용부호를 붙여야 하는 표현식이 있다면, pyparsing을 사용해 보시기를 권한다. 또하나의 진정한 보석으로서 최대한 아주 직관적으로 해석기를 만들 수 있다. (말이 나온김에, 이 모듈은 boost::spirit C++ 라이브러리와 비슷하다.)
  • 파이썬 위키에 가면 PythonSpeed 페이지에서 프로그램의 속도를 높일 수 있는 힌트와 그리고 그의 PerformanceTips 서브-페이지 보실 수 있다.
  • 속도에 관하여 Psyco가 흥미로운 모듈인데 이 모듈은 특히 쉽게 사용 가능한 확장 모듈로서 파이썬 코드의 실행 속도를 높일 수 있다 (의도적으로 이 설명에서 "엄청나게"라는 말을 빼버렸지만, 여전히 멋지다고 생각한다. 기본적으로 "공짜로" 속도를 얻을 수 있기 때문이다).
  • 일반적으로, 나는 이런 조각 코드 웹사이트를 좋아하지 않는다 (특히 할 수 없이 등록했더니 형편없는 코드를 발견했을 경우에는 더 그렇다). ASPN의 경우에는 상황이 아주 다르지만, 테이블을 예쁘게 인쇄하는 이 함수가 정말 마음에 든다. 자체로 설명이 충분하다. 코드를 보지 말고 행 리스트를 주자 (열 내용이 담긴 리스트). 그러면 테이블을 인쇄하기 위하여 레이아웃 처리된 문자열을 돌려줄 것이다.
  • 앤드류 쿠클링(Andrew Kuchling)은 파이썬의 유니코드에 관한 자습서를 집필했는데 이 문서는 IPython 메일링 리스트에서 추천하는 문서이다.
  • http://codespeak.net/py/current/doc/ - XP 테스트 작업틀

파이썬 콘솔에서의 자동완성

파이썬 프롬프트에서 파이썬 객체의 특성을 탭을 눌러 완성할 수 있으면 편리하다. 요즈음 나는 언제나 IPython을 사용하고 있는데. 파이썬 상호대화 콘솔을 상당히 개선하였으므로 정말 설치해 볼 가치가 있다. (자동완성과 들여쓰기 그리고 구문 강조와 마크로, 입력/출력 캐싱과 세션 관리 그리고 개선된 히스토리와 디버거, 역추적 등등;-) )
그렇지만, 흥미로운 점을 지적하자면 표준 파이썬 콘솔에서 자동완성이 가능하다는 것이다 (readline과 함께 컴파일되었다면 말이다. 실제로 그렇게 컴파일되었으리라 생각한다.)! 다음 코드는 그를 활성화시키기 위해 사용한다:
# .pythonrc.py
import readline, rlcompleter
readline.parse_and_bind("tab: complete")
이 코드를 ~/.pythonrc.py 등등의 파일에 넣고, PYTHONSTARTUP 변수를 이용하여 파이썬에게 그 위치를 알려준다! (즉, 나는 "exportPYTHONSTARTUP=$HOME/.pythonrc.py"를 쉘 환경 설정 안에 집어 넣었다.)
이제, 가서 IPython을 점검해 보자. ;-)

리스트에서 중복원소 제거하기

리스트에서 중복 원소를 제거하고 싶다면, 그냥 원소를 모조리 사전에 키로 집어 넣자 (예를 들어, 값은 None으로 한다). 그리고 dict.keys()를 점검하자. 이 방법을 최적화한 버전을 웹에서 발견하였다:
from operator import setitem
def distinct(l):
    d = {}
    map(setitem, (d,)*len(l), l, [])
    return d.keys()
map은 더 짧은 (이 경우 원소가 비어 있으면) 리스트를 None으로 채운다. 새로운 파이썬 버전에서는 위의 것보다 더 간결하게 표현할 수 있다:*
def distinct(l):
    return dict.fromkeys(l).keys() # 파이썬 2.3+에서 작동

def distinct24(l):
    return list(set(l)) # 파이썬 2.4 이상 필요
확실히 더 간결해서 더 손댈 필요없이 사용해도 좋을 정도이다. 지금까지 모든 변형은 두 가지 한계가 있다:
  • 원소들이 순서없이 반환된다.
  • 해시가 불가능한 원소(예, 부분리스트)를 가진 리스트는 처리가 안된다.

리스트 펴는 방법

왜 flatten 함수가 파이썬에 내장되지 않았는지 이유가 있다고 짐작한다. 왜냐하면 여러 의미구조적 질문이 제기되는데 어느 것도 직관적으로 답하지 못하기 때문인 것 같다:
  • 도대체 무엇을 편다는 것인가? 예를 들어 리스트 안에 있는 모든 리스트를 말하는 것인가? 아니면 터플도? 다른 연속열 유형은 어떤가 (위의 부분-클래스로서 손수-만든 벡터 등등, ...)? 아래에 본인의 flatter함수는 (문자열을 제외하고) 모든 반복가능 유형을 펴야 한다고 간주한다. 눈에 보이는 그대로 바꿀 수 있다. 예를 들어 isinstance-check에서 주석을 제거하면 된다.
  • 함수가 재귀적으로 작동하는가 아닌가? 대부분의 사람들은 함수가 실제로 flat 리스트를 돌려줄거라고 예상한다. 즉 안에 든 모든 연속열을 재귀적으로 펴서 돌려줄 것이라고 말이다.
그러나 파이썬 트릭에 관하여 말하고 있으므로, 먼저 본인의 해결책이 펴기 문제에 가장 흔하게 사용된다는 사실을 언급해야 하겠다:
# someLists 안에 있는 리스트의 모든 원소들을 someFunc에 건넨다:
someFunc(sum(someLists, []))
실제로, 이는 정확하게 본인이 필요한 유일한 펴기 방법이다. 그리고 깔끔한 제자리 해결책이다. 그러나 어쨌든, 다음은 완벽한 재귀 버전이다:
def flatten(x):
    """flatten(sequence) -> list

    하나로 펴진 리스트를 돌려준다. 
    안에는 연속열에서 열람한 원소들이 모두 담긴다.
    모두 재귀적으로 포함된 부분-연속열이다 (반복가능).

    예제:
    >>> [1, 2, [3,4], (5,6)]
    [1, 2, [3, 4], (5, 6)]
    >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, MyVector(8,9,10)])
    [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]"""

    result = []
    for el in x:
        #if isinstance(el, (list, tuple)):
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

부동소수점수 형식화

종종 부동소수점수를 멋지게 출력하기 위하여 올바른 형식화 플래그를 찾지 못하는 경우가 있다. 그리하여, 이 "작은" 표를 만들었다. 간단한 숫자와 함께 가능한 옵션 을 보여준다:
%s 0 1 3.14159265359 2.3 0.001 1e-10 100
%.3s 0 1 3.1 2.3 0.0 1e- 100
%r 0 1 3.1415926535897931 2.2999999999999998 0.001 1e-10 100
%f 0.000000 1.000000 3.141593 2.300000 0.001000 0.000000 100.000000
%.2f 0.00 1.00 3.14 2.30 0.00 0.00 100.00
%.f 0 1 3 2 0 0 100
%#.f 0. 1. 3. 2. 0. 0. 100.
%e 0.000000e+00 1.000000e+00 3.141593e+00 2.300000e+00 1.000000e-03 1.000000e-10 1.000000e+02
%.2e 0.00e+00 1.00e+00 3.14e+00 2.30e+00 1.00e-03 1.00e-10 1.00e+02
%#.e 0.e+00 1.e+00 3.e+00 2.e+00 1.e-03 1.e-10 1.e+02
%g 0 1 3.14159 2.3 0.001 1e-10 100
%#g 0.00000 1.00000 3.14159 2.30000 0.00100000 1.00000e-10 100.000
%.2g 0 1 3.1 2.3 0.001 1e-10 1e+02
%#.2g 0.0 1.0 3.1 2.3 0.0010 1.0e-10 1.0e+02
%.g 0 1 3 2 0.001 1e-10 1e+02
%#.g 0. 1. 3. 2. 0.001 1.e-10 1.e+02
슬프게도, 여전히 최적의 형식을 찾지 못했다. 내가 찾고 있는 방법은 신속하게
  1. pi를 3.14159로 보여주고 (즉, 소수 5 자리)
  2. 3.1을 3.1로 보여주며 (뒤에 0이 따르지 않음)
  3. 1.234e-13를 0으로 그리고 2.3e+02를 230으로 보여주는 것 (지수승은 보여주지 않는다)
  4. 이상적으로, -1.234e-13를 0으로도 보여주면 좋겠다 (-0이 아니라).
왜 "%#.2g" % 0.001 == '0.0010'인지 그 이유를 알려 주신다면, 진심으로 경청하겠다... ;-)

리스트의 반복/발생자를 거꾸로 처리하기

다음의 간편한 함수는 두 가지 목적을 충족한다. 첫 째, iter(somelist) 예제와 같이 편리하게 작성할 수 있다
for el in reviter(somelist):
    do_something(el)
둘 째, 놀랍도록 유용한 yield-구조를 상기시켜 준다 (yield는 2.3 버전에 도입되었다). 덕분에 발생자(generators)를 정의할 수 있다. 이는 생각건대 복잡한 반복자를 정의하려면 아주 자연스러운 방법이다:
def reviter(x):
    if hasattr(x, 'keys'):
        raise ValueError("사전은 역 반복을 지원하지 않는다")
    i = len(x)
    while i > 0:
        i -= 1
        yield x[i]
첫 yield 서술문에서 반복자로 기여하는 객체에 함수의 전체 상태가 저장된다. 반복자는 양보된 모든 값들을 돌려주고 최초의 함수가 돌아오면 StopIteration을 일으킨다.
이런 목적을 위하여 파이썬 2.4는 이미 "거꾸로 돌려준다".
발생자 함수 하나에 yield가 여러 개 있는 것도 - 실제로 아주 유용하며 - 가능하다. 즉, 다음 코드는 본인의 fig.py 모듈에서 가져 왔으며 6개의 점으로 열린/닫힌 다각형의 좌표 쌍을 작성하는데 사용된다 (한 행당 12개의 좌표). (yield외에도, map을 지혜롭게 사용하는 법도 보여준다. 행당 N=12 회 반복된 원소들을 무리짓는데 말이다):
class PolylineBase(Object):
    # ...

    def _savePointIter(self):
        # 점 리스트를 다음과 같이 편다: x1, y1, x2, y2, x2, ...
        for p in self.points:
            yield p[0]
            yield p[1]
        # 물론, yield도 조건적일 수 있다,
        # 여기에서는 닫힌 다각형의 첫 좌표쌍을 반복하는데 사용된다:
        if self.closed():
            yield self.points[0][0]
            yield self.points[0][1]

    def __str__(self):
        # ...
        i = self._savePointIter()
        # (i, )에 12를 곱해서 같은 반복자로부터 12 개의 참조점을 얻는데,
        # 반복자가 소진되면 map(None, ...)이
        # None으로 "채워 넣는다"는 사실을 이용한다:
        for linePoints in map(None, *(i, )*12):
            # linePoints는 이제 12 개의 좌표가 들어있다 (아니면 끝에 None 값이 들어 있다):
            result += "\t" + " ".join(*[str(p) for p in linePoints if p is not None]) + "\n"
        # ...

삼항 연산자(?:)가 그립다면

C-같은 언어로 프로그래밍하는데 익숙하고 파이썬 버전이 2.4 이하라면 아마도 if-then-else 연산이 그리울 것이다. 모든 경우는 아니라도 많은 경우 그 틈새를 메꾸어 줄 것이 있다: 불리언 연산자의 단축 회로 특성을 사용할 수 있다:
something = condition and true_value or false_value
condition에 "거짓"으로 평가되는 값이 있다면, and-연산자는 true_value를 평가하지 않고 "False"를 돌려준다. 실제로, 단순히 "False"를 돌려주는 것이 아니라,condition의 값을 돌려준다. 이 값은 일종의 False로 간주된다. 이렇게 특별한 방식으로 불리언 연산자가 작동하므로, 다시 말해 True나 False가 아니라 그냥 인자중의 하나를 돌려주기 때문에 true_value나 false_value를 변수 something에 할당할 수 있다.
주의!
위의 코드에서 true_value는 반드시 __nonzero__가 되어야 작동한다. 그렇지 않으면 이상한 결과를 얻는다:
this_will_be_two = cond and None or 2 # 틀렸다!
None은 연산자에 대하여 False와 같으므로 그 결과는 cond의 값에 상관없이 2가 된다. (대신에 not cond and 2 or None을 사용할 수도 있다 .)
읽기가 너무 어렵고 혼란스러우며 그리고 위험스러운 구문이다. 그러나 그럼에도 불구하고 어떤 경우는 편리하게 사용할 수 있다 ;-)
가끔 변종도 보인다
something = (false_value, true_value)[condition]
이 코드는 bool이 실제로 int에서 파생되었다는 사실을 이용한다. 그리고 False/True는 0/1이기도 하다 ( 조건이 0/1이 아니라면 bool(condition)으로 작성하자). 그렇지만, 여기에서 단축-회로는 전혀 없다. false_value와 true_value가 모두 평가될 것이고, 이는 다른 많은 사례에 적용하게 되면 빠질 만한 큰 함정이다.
왜 이 연산자가 빠졌는지 또 삼항 연산자를 애타게 찾는 사람이 당신이 처음인지 궁금하실 것이다; 물론 당신이 처음은 아니다. 그러나 오랫동안 파이썬 공동체는 삼항 연산자 구문에 합의하지 못했다. 그 토론은 PEP 308에서 보자. 그럼에도 불구하고, 귀도(Guido)는 마침내 삼항 연산자를 파이썬에 통합하기로 결정하고, 가장 인기있는 형태들 중에서true_value if condition else false_value를 골랐다 (이는 최종적으로 파이썬 2.5에 포함되었다).

Do-While 회돌이

파이썬은 do-while 또는 do-until 회돌이 구조가 (아직) 없기 때문에, 다음 관용구가 아주 일반적이 되었다:
while True:
    do_something()
    if condition():
        break
일관성 있게 고수하자. 그러면 조만간 아주 익숙해질 것이다.
[*] 패디 맥커씨(Paddy McCarthy)에게 이 모든 것에 되먹임을 주시고 새로운 파이썬 특징을 이용한 변형들을 가르쳐 주심에 고마움을 전한다.
[†] 그리고 피트 포먼(Pete Forman)에게 비-단축-회로 cond[bool] 변형을 알려주심을 감사를 드린다.


출처 : http://coreapython.hosting.paran.com/tiphack/python_tricks.htm

Articles