3. IPython 소개

About me

2014년 6월 파이썬 세미나 발표

  • Python data for analysis: 이 책의 3장(IPython) 공부한 내용을 중점적으로 설명
  • 내가 IPython Notebook 을 활용하면서 겪었던 문제나 활용팁
  • 기승전'파이참' 이었던 세미나
  • 시간이 30분 밖에 없어서 뒷부분은 후다닥 했던 기억..
  • 다들 알록달록한 쉘을 사용하고 있다는 걸 느낌(여쭤본 결과 fish shell을 사용하고 있고 새로 시작한다면 z shell을 추천한다고 하심.
  • 역시 개발자들은 공통적으로 귀차니즘이 심하다는 걸 느끼게 됨. 그래서 자동화를 좋아함. 공감되는 1인
  • 많은 고수분들의 다양한 경험을 들어서 좋았음.
  • 3시 넘어서 참석한게 아쉬움. 그 이전 발표도 들었으면 좋았겠지만 ppt를 보면서 따로 공부해야겠음.
  • Emacs가 가장 궁금했었음. 아쉽게도 발표할 노트북을 엄청 오래된(?) 노트북을 들고오셔서 d-sub 케이블 연결이 제대로 안됐음. 오래된 노트북으로도 Emacs가 돌아간다는걸 보여주시려고 하셨음. 거기에 충격과 공포. Emacs is my life.
  • Emacs에도 odd였나? IPython Notebook과 비슷한 것이 있다고 함. 아마 Emacs에서 가져온 개념이 아닐런지..?

어떤 환경에서 개발하나?

  • IPython과 PyCharm

IPython 설계 이념

  • 대화형 컴퓨팅
  • 소프트웨어 개발
  • 최적의 생산성을 얻도록 설계
  • 편집-컴파일-실행 방식보다 실행-탐색 방식 장려
  • 운영체제의 쉘 파일 시스템과 잘 통합(vi와 같이 바로 시스템 쉘 명령어를 사용할 수 있어 굉장히 편하다)
  • 이런 특징 덕분에 데이터 분석 프로그래밍에서 많은 부분을 차지하는 데이터 탐색, 실험, 오류 판독, 반복 등을 빠르게 처리
  • IPython 프로젝트는 진보된 대화형 파이썬 쉘 그 이상
  • GUI 콘솔에서 바로 확인할 수 있는 표와 웹 기반의 대화형 노트북 형식
  • 가볍고 빠른 병렬 컴퓨팅 엔진
  • 개발자를 위한 다른 도구와 마찬가지로 개인화(커스터마이징) 가능

Vim 익힐때처럼 직접 IPython도 사용해봐야 고수가 될 수 있다.


내가 IPython Notebook을 좋아하는 5가지 이유

  1. 소스 코드 작성하고 테스트를 바로 해볼 수 있고 따로 문서화를 하지 않아도 된다. 즉, 공부 + 정리가 한 번에 되므로 2마리 토끼를 한 번에 잡을 수 있다.
  2. Input, Output을 명확히 보여주기 때문에 따로 문서를 볼 때 input이 뭐지 위로 스크롤 올리지 않아도 된다.
  3. 문서화 기능이 아주 강력하다. 아주 짧은 스니펫 코드도 IPython Notebook으로 관리하면 깨끗하게 정리할 수 있다.
  4. github에 올리고 nbviewer만 앞에 붙여주면 바로 웹으로 볼 수 있기 때문에 공유 기능이 짱!
  5. 책을 보고 테스트 할 때 궁금한 사항을 바로 코드 위나 아래에 넣고 테스트 해볼 수 있다. 이 코드는 테스트 코드라고 쓸 수 있는 문서 기능까지 있으니 최고!
  6. gitst는 단편화 된 소스만 올릴 수 있고 github는 우리가 잘 알고 있는 git을 사용할 때 쓰는 사이트라고 이해하면 되겠다.

IPython Notebook 사전 지식

  1. Markdown 개념
  2. 단축키가 능수능란해야 문서 편집시 흐름이 끊기는 현상 발생 방지(Help - Keyboard Shortcuts을 보면 어떤 단축키들이 있는지 확인할 수 있으니 참고)
  3. Markdown CheatSheet

  4. Markdown syntax



데이터 분석에서 전처리가 차지하는 비율

  • 책:Data Mining Concepts and Techniques/데이터 전처리
  • 2. 모델링과 데이터의 전처리
  • 데이터 처리의 새로운 강자, dblyr 패키지: 데이터 분석에서 가장 많은 시간을 차지하는 것은 데이터를 분석에 필요한 형태로 만드는 데이터 전처리 과정입니다. 우리가 공부하면서 보게 되는 책에 있는 예제는 말 그대로 예제일 뿐이지 실제 데이터 분석 업무에서는 바로 모델링이나 시각화에 적합한 형태의 데이터를 얻기 위해서는 지루하고 복잡한 과정을 거치게 됩니다. 데이터 분석 프로젝트에 걸리는 시간의 절반 이상은 데이터의 전처리, 변환, 필터링이 차지하게 되는 것이 보통입니다.

3.1 IPython 기본

In [1]:
a = 5
In [2]:
a
Out[2]:
5
In [3]:
from numpy.random import randn
In [4]:
data = {i: randn() for i in range(7)}
In [5]:
data
Out[5]:
{0: -0.8743012242606852,
 1: -0.3503569994726426,
 2: -1.8293256174042911,
 3: -0.3581748222027181,
 4: 0.4674303701518697,
 5: 1.2963689779051482,
 6: -0.35058273321522254}

Python 쉘에서도 IPython처럼 print시 읽기 편한 형태로 보기

  • 대부분의 파이선 객체는 print를 이용한 보통의 출력 결과와는 달리 좀 더 읽기 편하거나 보기 좋은 형태로 출력된다.
  • 기본 파이썬에서는 아래와 같이 읽기 어렵게 출력된다.
>>> from numpy.random import randn
>>> data = {i: randn() for i in range(7)}
>>> data
{0: 0.15704031900582283, 1: 1.4572292651930963, 2: 0.670018652279804, 3: -0.28682608547409777, 4: 0.848409943727261, 5: -1.3015998921226193, 6: 0.2741861195113033}
>>> 
  • 이것을 IPython 처럼 예쁘게 보기 위해서는 pprint를 사용하면 된다.
>>> from pprint import pprint
>>> pprint(data)
{0: 0.15704031900582283,
 1: 1.4572292651930963,
 2: 0.670018652279804,
 3: -0.28682608547409777,
 4: 0.848409943727261,
 5: -1.3015998921226193,
 6: 0.2741861195113033}
>>> 

3.1.1 탭 자동 완성

  • 겉으로 보기에 IPython은 대화형 파이썬 인터프리터와는 조금 다르다.
  • Mathematica 사용자라면 번호가 붙어있는 입∙출력 프롬프트가 친숙하게 느껴질 것이다.
  • 쉘에서 입력을 하는 동안 Tab 을 누르면 네임스페이스에서 그 시점까지 입력한 내용과 맞아떨어지는 변수(객체, 함수 등)를 자동으로 찾아준다.
In [2]:
an_applet = 27
In [3]:
an_example = 42
In [3]: an<Tab>
an_applet   an_example  and         any    
  • 예제에서 파이썬 예약어인 and, 내장 함수인 any와 함께 위해서 따로 정의한 두 변수(an_apple, an_example)도 보여주는 것 확인
  • 당연히 어떤 객체의 메서드나 속성 뒤에 마침표를 입력한 후 자동 완성 기능을 활용
In [8]:
b = [1, 2, 3]
In [6]: b
%%bash      %bookmark   b           basestring  bin         bool        break       buffer      bytearray   bytes   
  • 모듈도 똑같이 작동
In [9]:
import datetime
In [8]: datetime.<Tab>
datetime.MAXYEAR        datetime.MINYEAR
datetime.date           datetime.datetime
datetime.datetime_CAPI  datetime.time
datetime.timedelta      datetime.tzinfo
  • 이것의 가장 좋은 장점은 모듈이름만 알고 있으면 따로 레퍼런스 문서를 찾아보지 않아도 어떤 기능들이 있는지 바로 확인할 수 있어 시간 절약하기에 굉장히 좋다.
  • < Tab >을 눌렀을 때 화면에 출력 결과가 너무 많으면 초보자는 헷갈릴 수 있는데, IPython은 아예 _로 시작하는 내부 메서드와 속성을 제외시키고 보여준다. 물론 먼저 _를 입력하면 해당 메서드와 속성을 선택할 수 있다. 기본으로 이런 메서드를 탭 자동 완성에 넣고 싶다면 IPython 환경 설정에서 설정 가능
  • 탭 자동 완성은 대화형 네임스페이스 검색과 객체 및 모듈 속성의 자동 완성뿐만 아니라 파일 경로(파이썬 문자열 안에서도)를 입력한 후 Tab을 누르면 입력한 문자열에 맞는 파일 경로를 컴퓨터의 파일 시스템 안에서 찾아서 보여준다.
In [9]: /tmp/<Tab>
/tmp/20140502022711/        /tmp/launch-cDn23Q/         /tmp/launch-sQsfKG/         /tmp/launchd-20363.VsYelO/  /tmp/launchd-64061.uaO30n/  
In [9]: path = '/tmp/<Tab>
/tmp/20140502022711/        /tmp/launch-cDn23Q/         /tmp/launch-sQsfKG/         /tmp/launchd-20363.VsYelO/  /tmp/launchd-64061.uaO30n/  
  • 나중에 살펴볼 %run 명령어와 함께 조합해서 사용하면 키 입력을 대폭 줄임
  • 또한 자동 완성 기능을 사용하면 함수에서 이름을 가진 인자도 = 기호까지 포함해서 보여준다.

3.1.2 자기관찰

  • 변수 이름 앞이나 뒤에 ? 기호를 붙이면 그 객체에 대한 일반 정보를 출력
In [5]:
b = [1,2,3]
In [9]:
b?
Type:        list
String form: [1, 2, 3]
Length:      3
Docstring:
list() -> new empty list
list(iterable) -> new list initialized from iterable's items
  • 이 기능은 객체의 자기관찰(Introspection) 이라고 하는데, 만약 객체가 함수거나 인스턴스 메서드라면 정의되어 있는 문서(docstring)을 출력
In [11]:
def add_numbers(a, b):
    """
    Add two numbers together
    
    Returns
    -------
    the_sum : type of arguments
    """
    return a + b
In [13]:
add_numbers?
Type:        function
String form: <function add_numbers at 0x10838d6e0>
File:        SPSE/<ipython-input-8-5b88597b2522>
Definition:  add_numbers(a, b)
Docstring:
Add two numbers together

Returns
-------
the_sum : type of arguments

SPSE 디렉토리 이야기

  • github에 올리기 전 임시로 SPSE 디렉토리에 정리를 했다.
  • github에 올린 뒤로는 ipython/python_data_analysis 로 정리했다.
  • 그러니 혹시 디렉토리 이름이 다른 경우가 있을 수 있으니 참고하기 바란다.

  • ??를 사용하면 함수의 소스코드를 보여준다.
In [14]:
add_numbers??
Type:        function
String form: <function add_numbers at 0x10838d6e0>
File:        /Users/re4lfl0w/Documents/SPSE/<ipython-input-8-5b88597b2522>
Definition:  add_numbers(a, b)
Source:
def add_numbers(a, b):
    """
    Add two numbers together

    Returns
    -------
    the_sum : type of arguments
    """
    return a + b

특수문자 ?와 *의 의미

  • 컴공쪽에서는 와일드 카드라고 한다.
  • ?는 0, 1
  • *는 0, 1, 1+
  • 한 번에 이해하기는 어려우니 예제를 살펴보면서 이해하자.
In [14]:
import numpy as np
In [15]:
np.*load*?
In [12]: np.*load*? np.load np.loads np.loadtxt np.pkgload
  • np.*: np 모듈의 앞에 어떤 글자가 와도 상관이 없고
  • load: 중간은 load가 반드시 들어가야 하며
  • *?: load 뒤에 여러 문자가 있어도 되고 없어도 된다.

  • np.load: 1번째, 2번째 , 3번째 ?에 의해 모두 생략될 수 있다. 단, load는 꼭 들어가야 한다.

  • np.loads: 2번째 *, 3번째 ?에 의해 load 뒤에 s가 첨부된 것도 찾을 수 있다.
  • np.loadtxt: 2번째 *, 3번째 ?에 의해 load 뒤에 txt가 첨부된 것도 찾을 수 있다.
  • np.pkgload: 1번째 *에 의해 load 앞에 pkg가 들어간 것도 찾을 수 있다.

이해하기 어렵다면 일단은 그냥 넘어가라. 이것은 정규표현식 부분을 공부해야 이해하기 편한 부분이다. 초보자는 그냥 이런것이 있다라고 생각만 하고 skip

3.1.3 %run 명령어

  • %run 명령어를 사용하면 IPython 세션 안에서 파이썬 프로그램을 불러와 실행 가능
In [19]:
!pwd
/Users/re4lfl0w/Documents/ipython/python_data_analysis

writefile 사용할 때 주의사항

  • 주석에 한글이 포함될 시 꼭 앞에 2줄을 붙여주자.

  • # !/usr/bin/python

  • # -*- coding: utf-8
  • 2줄이 없을시에 이런 에러를 확인할 수 있다.

>  File "/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_script_test.py", line 2
# writefile 이라는 매직 맹령어로 Notebook에서 디스크에 파일을 저장할 수도 있다.
                                                  ^
SyntaxError: Non-ASCII character '\xec' in file /Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_script_test.py on line 2, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
In [30]:
%%writefile ch03/ipython_script_test.py
# !/usr/bin/python
# -*- coding: utf-8
# writefile 이라는 매직 맹령어로 Notebook에서 디스크에 파일을 저장할 수도 있다.
# 책에서 파일이 있다고 가정하자고 해서 만들어 봤다.

def f(x, y, z):
    return (x + y) / z

a = 5
b = 6
c = 7.5

result = f(a, b, c)
print result
Overwriting ch03/ipython_script_test.py
In [31]:
%run ch03/ipython_script_test.py
1.46666666667
  • 스크립트 파일은 빈 네임스페이스(다른 변수가 선언되었거나 아무것도 import 되지 않은 상태)에서 실행되므로 명령행에서 python script.py 명령으로 실행한 것과 동일하게 동작
  • 스크립트 파일에서 정의된 모든 변수(import, 함수, 전역변수)는 실행된 후에 IPython에서 접근 가능
In [18]:
c
Out[18]:
7.5
In [19]:
result
Out[19]:
1.4666666666666666
  • 만약에 파이썬 스크립트에 명령행 인자(sys.argv에 저장되는)를 넘겨줘야 한다면 명령행에서 실행하는 것처럼 파일 경로 다음에 필요한 인자를 넘겨주면 된다.
  • 실행시키려는 스크립트 파일에서 IPython 네임스페이스에 미리 선언된 변수에 접근해야 한다면 %run 대신 %run -i 명령을 사용하자.

실행 중인 코드 중지하기

  • Ctrl+C를 누르면 KeyboardInterrupt 예외를 발생시켜 프로그램이 중지된다.
  • 가끔 Ctrl+C를 눌러도 안될 때가 있는데 이때는 파이썬 프로세스를 강제 종료하자.

3.1.4 클립보드에 있는 코드 실행하기

  • 클립보드에 있는 내용을 IPython 쉘에 붙여넣기 할 때 주의할 점은, 줄 바꿈을 return으로 처리한다. 이 때문에 들여쓰기 된 블록 안의 빈 줄을 IPython은 블록이 끝난 것으로 인식한다.
  • 이것을 방지하기 위해서는 %paste 명령을 써주면 된다.
  • %paste는 클립보드에 있는 내용을 붙여넣기하고 바로 끝낸다.
  • %cpaste는 붙여넣기 할 수 있는 공간을 열어준다고 보면된다. 사용자가 --를 해주기 전까지 입력값을 종료하지 않는다. 실행해 보기 전에 마음껏 붙여넣기 해볼 수 있다는 것을 뜻한다.
x = 5
y = 7
if x > 5:
    x += 1

    y = 8
In [25]: %paste
x = 5
y = 7
if x > 5:
    x += 1

    y = 8

## -- End pasted text --
In [26]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:x = 5
:y = 7
:if x > 5:
:    x += 1
:    
:    y = 8
:
:--

IDE, 편집기와 함께 IPython 사용하기

  • 이맥스나 vim 같은 몇몇 편집기는 서드파티 확장을 통해 실행중인 IPython 으로 코드 블록을 직접 보낼 수 있다. IPython 웹사이트나 인터넷 검색을 통해 좀 더 많은 정보를 얻을 수 있다.

3.1.5 키보드 단축키

  • IPython은 이맥스 편집기나 유닉스 Bash 사용자에게 친숙한 프롬프트 이동 단축키 제공
  • 이전에 입력한 쉘의 명령어 히스토리 사용 가능
  • 거의 Bash 쉘과 비슷하다고 보면 된다. 이전에 쉘 공부하신 분들은 거의 똑같으니 금방 익힐 수 있다.
  • GNU Readline Library 1.2.1 Readline Bare Essentials
  • 내가 자주 사용하는 것은 위 화살표, 아래 화살표다.

3.1.6 예외와 트레이스백

  • %run 명령을 사용하면 스크립트나 일반 파이썬 코드를 실행하는 중에 오류가 발생했을 때 전체 스택 정보를 각 위치별 문맥 정보와 함께 볼 수 있다.
In [2]:
# %run ch03/ipython_bug.py
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_bug.py in <module>()
     13     throws_an_exception()
     14 
---> 15 calling_things()

/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_bug.py in calling_things()
     11 def calling_things():
     12     works_fine()
---> 13     throws_an_exception()
     14 
     15 calling_things()

/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_bug.py in throws_an_exception()
      7     a = 5
      8     b = 6
----> 9     assert(a + b == 10)
     10 
     11 def calling_things():

AssertionError: 
  • 일반 파이썬 인터프리터에서는 추가 문맥 정보를 제공하지 않지만 IPython에서는 추가 정보 제공
  • %xmode 매직 명령어를 이용해서 표시되는 문맥 정보의 양을 조절할 수 있는데 작게는 일반 파이썬 인터프리터 수준
  • 만헥는 함수의 인자 값과 그 이상의 정보를 보여주도록 설정 가능
  • 오류가 발생한 직후 대화형 디버거 사용을 위해 %debug나 %pdb 매직 명령어를 이용해서 스택 안을 살펴볼 수도 있다.

3.1.7 매직 명령어

  • IPython은 '매직' 명령어라고 하는 여러 가지 특수한 명령어를 포함하고 있는데, 이는 일반적인 작업이나 IPython 시스템 동작을 쉽게 제어할 수 있도록 설계된 특수한 명령어
  • 매직 명령어는 앞에 % 기호를 붙이는 형식
  • 예를 들어 IPython에서 행렬 곱셈 같은 코드가 실행된 시간을 측정하고 싶을 때 %timeit 매직 함수를 이용해서 값을 얻어올 수 있다.
In [32]:
a = np.random.randn(100, 100)
In [33]:
a
Out[33]:
array([[-1.24795852,  1.54416008,  0.33070124, ...,  0.78562341,
         0.09254828,  0.8429892 ],
       [-0.68266363,  0.4380124 ,  1.04945737, ...,  1.71421051,
        -2.10686509, -0.26068562],
       [-1.44114179, -0.58686428,  2.04138524, ...,  0.08009878,
         0.73308772, -2.6788362 ],
       ..., 
       [-1.19584095, -0.04156138, -2.40651689, ..., -0.93047556,
         0.74903251, -0.69777648],
       [ 0.52452989,  1.36099329,  0.33302706, ...,  0.88645082,
         1.44052124,  1.28476217],
       [ 0.60878216,  1.09719752, -0.51406306, ..., -0.19358492,
        -0.04897423, -0.85155121]])
In [34]:
%timeit np.dot(a, a)
10000 loops, best of 3: 95.8 µs per loop

timeit의 loops 횟수에 대한 생각

  • loops는 거의 비슷한 시간이 걸리도록 loops 횟수를 IPython에서 조절하는 것으로 판단된다.
  • 책과 상이한 loops 횟수를 보이는 곳이 가끔 있으니 이런 것이 있다 정도만 인식하고 넘어가자.
  • 매직 명령어는 IPython 시스템 안에서 실행되는 명령행 프로그램
  • 매직 명령어들은 추가적인 옵션을 필요로 하며 ?를 이용해서 전체 옵션 보기 가능
In [35]:
%reset?
Type:        Magic function
String form: <bound method NamespaceMagics.reset of <IPython.core.magics.namespace.NamespaceMagics object at 0x101cbcb90>>
Namespace:   IPython internal
File:        /Library/Python/2.7/site-packages/IPython/core/magics/namespace.py
Definition:  %reset(self, parameter_s='')
Docstring:
Resets the namespace by removing all names defined by the user, if
called without arguments, or by removing some types of objects, such
as everything currently in IPython's In[] and Out[] containers (see
the parameters for details).

Parameters
----------
-f : force reset without asking for confirmation.

-s : 'Soft' reset: Only clears your namespace, leaving history intact.
    References to objects may be kept. By default (without this option),
    we do a 'hard' reset, giving you a new session and removing all
    references to objects from the current session.

in : reset input history

out : reset output history

dhist : reset directory history

array : reset only variables that are NumPy arrays

See Also
--------
reset_selective : invoked as ``%reset_selective``

Examples
--------
::

  In [6]: a = 1

  In [7]: a
  Out[7]: 1

  In [8]: 'a' in _ip.user_ns
  Out[8]: True

  In [9]: %reset -f

  In [1]: 'a' in _ip.user_ns
  Out[1]: False

  In [2]: %reset -f in
  Flushing input history

  In [3]: %reset -f dhist in
  Flushing directory history
  Flushing input history

Notes
-----
Calling this magic from clients that do not implement standard input,
such as the ipython notebook interface, will reset the namespace
without confirmation.
  • 매직 함수와 같은 이름의 변수가 선언되지 않았다면 기본적으로 % 기호 없이도 매직 함수를 사용할 수 있다. 이를 오토매직(Automagic) 이라고 하는데, %automatic을 이용해서 이 기능을 켜거나 끌 수 있다.
  • %quickref나 %magic 명령을 통해서 사용 가능한 모든 특수 명령어를 살펴봐두는 것이 좋다.

자주 사용하는 IPython 매직 명령어

In [25]:
%%writefile ch03/data.csv
Command/ Comment
%quickref/ IPython의 빠른 도움말 표시
%magic/ 모든 매직 함수에 대한 상세 도움말 출력
%debug/ 최근 예외 트레이스백의 하단에서 대화형 디버거로 진입
%hist/ 명령어 입력(그리고 선택적으로 출력) 히스토리 출력
%pdb/ 예외가 발생하면 자동적으로 디버거로 진입
%paste/ 클립보드에서 들여쓰기가 된 채로 파이썬 코드 가져오기
%cpaste/ 실행 파이썬 코드를 수동으로 붙여넣을 수 있는 프롬프트 표시
%reset/ 대화형 네임스페이스에서 정의된 모든 변수와 이름을 삭제
%page/ OBJECT 객체를 pager를 통해 보기 좋게 출력
%run script.py/ IPython 내에서 파이썬 스크립트 실행
%prun statement/ cProfile을 통해 statement를 실행하고 프로파일링 결과를 출력
%time statement/ 단일 statement 실행 시간을 출력
%timeit statement/ statement를 여러 차례 실행한 후 평균 실행 시간을 출력. 
 / 매우 짧은 시간 안에 끝나는 코드의 시간을 측정할 때 유용
%who, %who_ls, %whos/ 대화형 네임스페이스 내에서 정의된 변수를 다양한 방법으로 표시
%xdel variable/ variable을 삭제하고 IPython 내부적으로 해당 객체에 대한 모든 참조를 제거
Overwriting ch03/data.csv
In [26]:
import pandas as pd
In [27]:
pd.core.format.set_printoptions(notebook_repr_html=True)
/Library/Python/2.7/site-packages/pandas-0.12.0_307_g3a2fe0b-py2.7-macosx-10.8-intel.egg/pandas/core/format.py:1656: FutureWarning: set_printoptions is deprecated, use set_option instead
  FutureWarning)
In [28]:
df = pd.read_csv('ch03/data.csv', delimiter='/')
num Command Comment
1 nice good
2 aaaa bbb
In [29]:
df
Out[29]:
Command Comment
0 %quickref IPython의 빠른 도움말 표시
1 %magic 모든 매직 함수에 대한 상세 도움말 출력
2 %debug 최근 예외 트레이스백의 하단에서 대화형 디버거로 진입
3 %hist 명령어 입력(그리고 선택적으로 출력) 히스토리 출력
4 %pdb 예외가 발생하면 자동적으로 디버거로 진입
5 %paste 클립보드에서 들여쓰기가 된 채로 파이썬 코드 가져오기
6 %cpaste 실행 파이썬 코드를 수동으로 붙여넣을 수 있는 프롬프트 표시
7 %reset 대화형 네임스페이스에서 정의된 모든 변수와 이름을 삭제
8 %page OBJECT 객체를 pager를 통해 보기 좋게 출력
9 %run script.py IPython 내에서 파이썬 스크립트 실행
10 %prun statement cProfile을 통해 statement를 실행하고 프로파일링 결과를 출력
11 %time statement 단일 statement 실행 시간을 출력
12 %timeit statement statement를 여러 차례 실행한 후 평균 실행 시간을 출력.
13 매우 짧은 시간 안에 끝나는 코드의 시간을 측정할 때 유용
14 %who, %who_ls, %whos 대화형 네임스페이스 내에서 정의된 변수를 다양한 방법으로 표시
15 %xdel variable variable을 삭제하고 IPython 내부적으로 해당 객체에 대한 모든 참조를 제거

3.1.8 Qt 기반의 GUI 콘솔

  • 터니널 전용 애플리케이션에 그림을 삽입하거나 여러 줄을 편집하거나 문법을 돋보이게 하는 등의 리치 텍스트 위젯 기능을 추가하기 위해 Qt 프레임워크 기반의 GUI 콘솔 개발

IPython에서 그래프 기능 내장하여 실행 방법

  • 전제 조건: PyQt나 PySide가 설치되어 있어야 한다.

    $ ipython qtconsole --pyalb=inline

  • Qt 콘솔에서는 여러 개의 IPython 프로세스를 탭으로 구분해서 실행

  • IPython HTML 노트북 애플리케이션과도 함께 사용(현재 이 문서를 작성한 것)

3.1.9 Pylab 모드와 Matplotlib 통합

  • IPython이 과학계산 컴퓨팅 영역에서 널리 사용되고 있는 이유: matplotlib 및 다른 GUI 툴킷 같은 라이브러리와 함께 사용하기 쉽다는 점
  • 일반 파이썬에서 matplotlib을 이용해서 그래프 윈도우를 만든다면 GUI 이벤트 루프가 그래프 윈도우가 닫힐 때까지 파이썬 세션의 제어권을 가로챈다. But Ipython은 각각의 GUI 프레임워크를 잘 이용해서 내부에서도 멈추지 않고 작업을 계속할 수 있도록 구현

IPython을 matplotlib와 함께 사용하려면 --pylab 옵션 설정하고 실행

$ ipython --pylab

  • 이렇게 실행하면 IPython은 기본 GUI 백엔드와 통합되어 matplotlib 그래프 윈도우가 문제없이 실행
  • %gui 명령을 이용해 대화형 컴퓨팅 환경 설정 가능

3.2 명령어 히스토리 사용하기

  • 검색, 자동 완성, 최소한의 입력으로 이전에 실행했던 명령 재실행
  • 세션 간의 명령어 히스토리 유지
  • 입∙출력 히스토리를 파일에 기록

3.2.1 명령어 검색과 재사용

명령어 자주 사용

%run first/second/third/data_script.py

  • 위 화살표 키나, Ctrl + P를 누르면 이전에 실행했던 명령어 실행(계속 눌러보면서 익숙해지기)
  • 아래 화살표 키나 Ctrl + B를 누르면 목록을 거꾸로 보여주며 최신의 실행 결과를 보여줌. 즉, 위 화살표 키 반대 기능

Ctrl+R을 누르면 부분 순차 검색 사용

3.2.2 입∙출력 변수

  • 함수의 실행 결과를 변수에 저장하지 못하면 매우 불편
  • 입력한 내용과 출력된 결과물(반환된 객체) 둘 다 특수한 변수에 저장
  • 마지막 2개의 결과를 저장하는 바로 이전 결과는 _ 변수에, 그 이전 결과는 __ 변수에 저장한다.

_ 변수 사용시 주의사항

  • 바로 이전 결과를 저장하기 때문에 IPython의 명령어 실행된 번호가 매우 중요하다.
  • 현재 In [117] 에서 _ 명령어를 입력했다면 In [116] 결과값이 저장된 것이다.
In [30]:
2 ** 27
Out[30]:
134217728
In [31]:
_
Out[31]:
134217728
  • 입력 변수: _iX(X는 입력 줄 번호) 변수에 저장
  • 출력 변수: _X
  • 그래서 119번 줄을 입력한 후에는 출력 결과를 저장하는 _119 변수와 입력변수인 _i119이 생겨난다.
In [32]:
foo = 'bar'
In [33]:
foo
Out[33]:
'bar'
In [3]:
# _i119
In [4]:
# _119
In [7]:
# 값을 설정
# _i118
In [8]:
# Failed! 118번 명령줄에 출력값이 없기 때문
# _118
  • 입력 변수는 문자열이기 때문에 다음처럼 파이썬의 exec 예약어를 사용해서 다시 실행

    exec _119

In [9]:
# exec _i119

매직 함수를 사용한 입∙출력 히스토리를 사용한 작업

In [ ]:
%hist
In [ ]:
%reset
In [ ]:
%xdel result
In [35]:
result
Out[35]:
1.4666666666666666
  • 매우 큰 데이터를 이용하는 작업을 할 때는 del 예약어를 사용해서 인터랙티브 네임스페이스에서 어떤 변수를 지웠다고 해도 IPython의 입∙출력 히스토리가 가비지 콜렉트(메모리 정리)를 방해할 수 있다는 점을 기억
  • 그런 경우에는 %xdel과 %reset을 신중하게 사용해서 메모리 문제 회피

3.2.3 입∙출력 기록하기

  • 입∙출력을 포함한 전체 콘솔 세션의 로그를 %logstart를 사용해서 기록할 수 있다.
  • 로깅은 아무 때나 활성화 시킬 수 있으며 과거 명령을 포함한 전체 세션을 기록
  • 따라서 뭔가 작업을 하고 있다가 그때까지 작업한 모든 내용을 저장하고 싶다면 로깅을 활성화 하면 된다.
  • %logoff
  • %logon
  • %logstate
  • logstop
In [36]:
%logstart
Activating auto-logging. Current session state plus future input saved.
Filename       : ipython_log.py
Mode           : rotate
Output logging : False
Raw input log  : False
Timestamping   : False
State          : active
In [37]:
%logstop
In [38]:
%logstart?
Type:        Magic function
String form: <bound method LoggingMagics.logstart of <IPython.core.magics.logging.LoggingMagics object at 0x101cbcb50>>
Namespace:   IPython internal
File:        /Library/Python/2.7/site-packages/IPython/core/magics/logging.py
Definition:  %logstart(self, parameter_s='')
Docstring:
Start logging anywhere in a session.

%logstart [-o|-r|-t] [log_name [log_mode]]

If no name is given, it defaults to a file named 'ipython_log.py' in your
current directory, in 'rotate' mode (see below).

'%logstart name' saves to file 'name' in 'backup' mode.  It saves your
history up to that point and then continues logging.

%logstart takes a second optional parameter: logging mode. This can be one
of (note that the modes are given unquoted):

append
    Keep logging at the end of any existing file.

backup
    Rename any existing file to name~ and start name.

global
    Append to  a single logfile in your home directory.

over
    Overwrite any existing log.

rotate
    Create rotating logs: name.1~, name.2~, etc.

Options:

  -o
    log also IPython's output. In this mode, all commands which
    generate an Out[NN] prompt are recorded to the logfile, right after
    their corresponding input line. The output lines are always
    prepended with a '#[Out]# ' marker, so that the log remains valid
    Python code.

  Since this marker is always the same, filtering only the output from
  a log is very easy, using for example a simple awk call::

    awk -F'#\[Out\]# ' '{if($2) {print $2}}' ipython_log.py

  -r
    log 'raw' input.  Normally, IPython's logs contain the processed
    input, so that user lines are logged in their final form, converted
    into valid Python.  For example, %Exit is logged as
    _ip.magic("Exit").  If the -r flag is given, all input is logged
    exactly as typed, with no transformations applied.

  -t
    put timestamps before each input line logged (these are put in
    comments).

3.3 운영체제와 함께 사용하기

  • 또 다른 중요한 기능: 운영체제 쉘과 강력하게 통합
  • IPython을 종료하지 않고 윈도우나 유닉스 쉘이 일반적인 명령행에서 할 수 있는 작업이 가능하다는 뜻(vi와 비슷)
  • 쉘 명령어를 실행하거나 디렉토리를 옮기거나 명령어의 결과를 파이썬 객체(리스트나 문자열)에 저장하는 기능이 포함
  • 또한 간단한 쉘 명령어 앨리어싱과 디렉토리 북마크 기능이 제공

IPython reference Command-line usage

3.3.1 쉘 명령어와 별칭

  • !로 시작하는 줄은 느낌표 다음에 있는 내용을 시스템 쉘에서 실행하라는 의미
  • rm이나 del 명령어를 사용해서 파일을 지우거나 디렉토리를 옮기거나 다른 프로세스를 실행할 수 있다는 말
  • 심지어 다른 파이썬 인터프리터와 같은 IPython의 제어권을 빼앗아가는 프로세스도 실행 가능
In [39]:
# 사용자마다 환경이 다를 수 있으니 이 명령어가 제대로 동작하지 않을 수 있음
ip_info = !ifconfig en0 | grep 'inet'
In [40]:
ip_info[1].strip()
Out[40]:
'inet 192.168.10.31 netmask 0xffffff00 broadcast 192.168.10.255'
In [41]:
# 정규표현식을 사용한 ip 주소만 추출 예제
# 그냥 이렇게도 할 수도 있다는 것만 알아두세요.
# 너무 깊이 들어가면 머리 아픕니다.

import re
ip = re.search('inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', ip_info[1].strip())
print ip.group(1)
192.168.10.31
  • !를 사용해서 현재 환경에 정의되어 있는 파이썬 값을 대체하기도 한다.
  • 변수 이름 앞에 달러 기호 $를 붙이면 된다.
  • 좀 어렵게 설명되어 있는데 쉽게 예를 들어서 foo라는 변수를 IPython에서 정의하고 !를 사용해 쉘 명령어를 사용할 때 파라미터 값으로 사용할 수 있다는 말이다. 음..? 내가 더 어렵게 설명했나? 그래도 예제를 보면 이해하리라 생각한다.
In [42]:
foo = 'test*'
In [43]:
!ls $foo
ls: test*: No such file or directory
In [44]:
# 위의 명령어와 똑같은데 foo라는 변수를 사용했느냐? 아니냐? 의 차이다.
# 변수를 사용하면 나중에 똑같은 작업을 하지 않을 수 있으니 재활용성 측면에서 매우 좋다.
# 하드 코딩은 나중에 수정하기가 매우 어려우니 변수 쓰는 습관을 들이자.
!ls test*
ls: test*: No such file or directory
  • %alias: 쉘 명령어에 대한 사용자 단축키(별칭)을 정의
In [45]:
# ll: ls -l로 정의하겠다.
# ls가 무엇인가? 해당 디렉토리에 어떤 파일들과 디렉토리들이 있는지 확인하는 쉘 명령어
# 이렇게 단축키를 사용하면 ls -l 입력할 것을 ll만 입력하면 되기 때문에 시간 절약
%alias ll ls -l
In [46]:
ll /usr
total 8
lrwxr-xr-x     1 root  wheel      8 Jul 24  2013 X11R6 -> /opt/X11
drwxr-xr-x  1067 root  wheel  36278 Feb 26 05:50 bin
drwxr-xr-x   281 root  wheel   9554 Dec 20 11:53 include
drwxr-xr-x   323 root  wheel  10982 Dec 20 11:53 lib
drwxr-xr-x   147 root  wheel   4998 Dec 24 20:43 libexec
drwxr-xr-x     3 root  wheel    102 Dec 20 11:51 llvm-gcc-4.2
drwxrwxr-x    19 root  admin    646 Jan  9 14:29 local
drwxr-xr-x   267 root  wheel   9078 Dec 20 11:51 sbin
drwxr-xr-x    54 root  wheel   1836 Dec 22 05:39 share
drwxr-xr-x     4 root  wheel    136 Jul 23  2013 standalone
  • 쉘과 똑같이 사용할 수 있으니 ;으로 구분하여 한 번에 실행할 수도 있다.
In [47]:
%alias test_alias (cd /tmp; ls;)
In [48]:
test_alias
VMwareDnD          launch-BK2w6d      launch-dDPynA      launchd-336.7UVzxV
launch-8m9D1N      launch-EZ4J4M      launch-z9jhMT      mysql.sock
  • 세션이 종료됨과 동시에 정의해놓은 별칭이 없어짐. 휘발성
  • 고정 별칭을 만들고 싶다면 설정 시스템 사용

3.3.2 디렉토리 북마크 시스템

  • 브라우저의 즐겨찾기와 똑같다고 생각하면 된다.
In [49]:
# %bookmark db /Users/re4lfl0w/Dropbox/
  • 북마크를 해두고 나면 %cd 매직을 이용할 때 사용할 수 있다.
  • 앞의 %가 생략되어도 오토매직으로 인하여 쉘 명령어가 실행될 수 있다고 앞에 언급하였다.
In [50]:
pwd
Out[50]:
u'/Users/re4lfl0w/Documents/ipython/python_data_analysis'
In [51]:
# cd db
In [52]:
pwd
Out[52]:
u'/Users/re4lfl0w/Documents/ipython/python_data_analysis'
In [53]:
%bookmark -l
Current bookmarks:
cp -> ~/downloads/_tmp/ppt
db -> /Users/re4lfl0w/Dropbox/
  • 별칭과 달리 북마크는 IPython이 종료되더라도 유지된다.

3.4 소프트웨어 개발 도구

  • 데이터 분석 애플리케이션에서는 올바른 코드가 가장 중요
  • IPython은 향상된 파이썬 pdb 디버거를 내장
  • 코드 실행이 빨라야 하는데 IPython은 쉽게 사용할 수 잇는 코드 타이밍과 프로파일링 도구를 포함

디버거 내용은 어렵기 때문에 초보자들은 skip 해도 무방하리라 본다.

  • 나중에 중∙고수가 되었을 때 다시 한 번 살펴보라.
  • 어찌됐든 내가 함수를 만들면 버그가 없게 만들려면 디버깅 과정은 필수니..
  • 지금은 따라하는 수준이기 때문에 굳이 어려운 걸 배우느라 재미를 포기할 필요는 없다는게 개인적인

현재 이 ipynb 파일을 다운받아 실행해 볼 때 debugger가 실행되기 때문에 명령어가 입력되지 않는 상황이 발생하니 주의. quit로 빠져 나오면 된다.

파이썬 디버깅 참고 자료

  1. 파이썬 상호대화 디버깅
  2. 파이썬 디버깅
  3. Getting started with the Python Debugger pdb
  4. pdb - Interactive Debugger

Video

  1. Youtube search word: "python debugger"
  2. Introduction to PDB
  3. Fundementals of Python Debugging

3.4.1 인터랙티브 디버거

  • IPython의 디버거는 pdb에 탭 자동 완성 기능과 문법 강조, 예외 트레이스백에서 각 줄에 해당하는 컨택스트 개선
  • 오류가 발생한 바로 그 시점이 코드를 디버깅하기에 가장 최적인 시점
  • 예외가 발생한 후에 %debug 명령어를 사용하면 사후처리(post-mortem) 디버거가 실행되고 예외가 발생한 시점의 스택 프레임 정보를 보여준다.
In [13]:
# %run ch03/ipython_bug.py
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_bug.py in <module>()
     13     throws_an_exception()
     14 
---> 15 calling_things()

/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_bug.py in calling_things()
     11 def calling_things():
     12     works_fine()
---> 13     throws_an_exception()
     14 
     15 calling_things()

/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/ipython_bug.py in throws_an_exception()
      7     a = 5
      8     b = 6
----> 9     assert(a + b == 10)
     10 
     11 def calling_things():

AssertionError: 
In [55]:
# %debug
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(9)throws_an_exception()
      8     b = 6
----> 9     assert(a + b == 10)
     10 

ipdb> u
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(13)calling_things()
     12     works_fine()
---> 13     throws_an_exception()
     14 

ipdb> u
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(15)<module>()
     13     throws_an_exception()
     14 
---> 15 calling_things()

ipdb> u
> /Library/Python/2.7/site-packages/IPython/utils/py3compat.py(224)execfile()
    223                 filename = fname
--> 224             builtin_mod.execfile(filename, *where)
    225 

ipdb> u
*** Oldest frame
ipdb> d
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(15)<module>()
     13     throws_an_exception()
     14 
---> 15 calling_things()

ipdb> d
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(13)calling_things()
     12     works_fine()
---> 13     throws_an_exception()
     14 

ipdb> d
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(9)throws_an_exception()
      8     b = 6
----> 9     assert(a + b == 10)
     10 
  • 디버거 안에서는 아무 파이썬 코드나 실행해볼 수도 있고
  • 각각의 스택 프레임에서 인터프리터 안에 유지되고 있는 모든 객체와 데이터를 살펴볼 수도
  • 디폴트로 오류가 발생한 가장 아래 레벨에서 시작한다.
  • u(up), d(down)를 눌러서 스택 트레이스 사이 이동 가능

  • %pdb: 예외가 발생할 경우 IPython이 자동적으로 디버거를 실행하는데, 이 모드는 많은 사용자들이 아주 유용하다고 생각

  • 디버거는 개발하는 중에 스크립트나 함수를 실행하는 과정에서 각 단계를 하나씩 검증하거나 브레이크포인트를 설정하고 싶을 때 쉽게 사용
  • %run 명령에 -d 옵션: 스크립트를 실행하기 전에 디버거를 먼저 실행. 그리고 s(step)를 누르면 스크립트로 진입
Breakpoint 1 at /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py:1
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(1)<module>()
1---> 1 def works_fine():
      2     a = 5
      3     b = 6

ipdb> s
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(6)<module>()
      5 
----> 6 def throws_an_exception():
      7     a = 5
In [56]:
run -d ch03/ipytch03/ipytch03/ipytch03/ipytch03/ipython_bug.py
ERROR: File `u'ch03/ipytch03/ipytch03/ipytch03/ipytch03/ipython_bug.py'` not found.
  • 여기서부터는 스크립트 파일을 어떤 식으로 동작하게 할 것인가는 독자의 몫
  • 예를 들어 위 예제에서 works_fine 메서드를 호출하기 바로 직전에 브레이크포인트를 걸고 c(continue)를 눌러 브레이크포인트에 걸릴 때까지 스크립트를 실행할 수 있다.
ipdb> b 12
Breakpoint 2 at /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py:12
ipdb> c
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(12)calling_things()
     11 def calling_things():
2--> 12     works_fine()
     13     throws_an_exception()

이제 s를 눌러 works_fine() 안으로 진입하거나 n(next)을 눌러 works_fine()을 실행하고 다음 줄로 진행할 수도 있다.

ipdb> n
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(13)calling_things()
2    12     works_fine()
---> 13     throws_an_exception()
     14 
  • 그러면 이제는 throws_an_exception까지 진입했고 올류가 발생하는 다음 줄로 진행한 후 해당 범위 안에 있는 변수를 살펴보자.
  • 디버거 명령어는 변수 이름보다 우선이므로 디버거 명령과 같은 이름의 변수가 있다면 !를 변수 이름 앞에 붙여서 내용을 확인할 수 있다.
ipdb> s
--Call--
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(6)throws_an_exception()
      5 
----> 6 def throws_an_exception():
      7     a = 5

ipdb> n
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(7)throws_an_exception()
      6 def throws_an_exception():
----> 7     a = 5
      8     b = 6

ipdb> n
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(8)throws_an_exception()
      7     a = 5
----> 8     b = 6
      9     assert(a + b == 10)

ipdb> n
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(9)throws_an_exception()
      8     b = 6
----> 9     assert(a + b == 10)
     10 

ipdb> !a
5
ipdb> !b
6
  • 대화형 디버거는 많은 연습과 시행착오를 통해서만 익숙해질 수 있다.
  • IDE에도 훌륭한 GUI 디버거를 내장하고 있지만 IPython 디버거를 사용해서 디버깅을 하는 것이 생산성 향상에 많은 도움
In [57]:
%%writefile ch03/debugger.csv
Command, Comment
h(elp), 명령어 목록을 보여준다.
help command, command에 대한 문서를 보여준다.
c(continue), 프로그램의 실행을 재개한다.
q(uit), 더 이상 코드를 실행하지 않고 디버거를 종료한다.
b(reak) number, 현재 파일의 number번째 줄에 브레이크포인트를 설정한다.
b path/to/file.py:number, 지정한 파일의 number번째 줄에 브레이크포인트를 설정한다
s(tep), 함수 호출 안으로 진입한다.
n(ext), 현재 줄을 실행하고 같은 레벨의 다음 줄로 진행한다.
u(p) / d(own), 함수 콜 스택(호출 정보)의 위/아래로 이동한다.
a(rgs), 현재 함수의 인자를 보여준다.
debug statement, statement 문장을 새로운 (재귀적) 디버거에서 실행한다.
l(ist) statement, 현재 위치와 스택의 현재 레벨에 대한 문맥을 보여준다.
w(here), 현재 위치에 대한 문맥과 함께 전체 스택 정보를 출력한다.
Overwriting ch03/debugger.csv
In [58]:
df = pd.read_csv('ch03/debugger.csv', delimiter=',')
In [59]:
df
Out[59]:
Command Comment
0 h(elp) 명령어 목록을 보여준다.
1 help command command에 대한 문서를 보여준다.
2 c(continue) 프로그램의 실행을 재개한다.
3 q(uit) 더 이상 코드를 실행하지 않고 디버거를 종료한다.
4 b(reak) number 현재 파일의 number번째 줄에 브레이크포인트를 설정한다.
5 b path/to/file.py:number 지정한 파일의 number번째 줄에 브레이크포인트를 설정한다
6 s(tep) 함수 호출 안으로 진입한다.
7 n(ext) 현재 줄을 실행하고 같은 레벨의 다음 줄로 진행한다.
8 u(p) / d(own) 함수 콜 스택(호출 정보)의 위/아래로 이동한다.
9 a(rgs) 현재 함수의 인자를 보여준다.
10 debug statement statement 문장을 새로운 (재귀적) 디버거에서 실행한다.
11 l(ist) statement 현재 위치와 스택의 현재 레벨에 대한 문맥을 보여준다.
12 w(here) 현재 위치에 대한 문맥과 함께 전체 스택 정보를 출력한다.

다른 방법

  • 디버거를 실행하는 몇 가지 다른 유용한 방법
  • set_trace 함수(pdb.set_trace)를 이용하는 것이 그 중 하나로, '가난뱅이의 브레이크포인트'라고 불린다.
  • 이 코드를 범용적으로 사용하기 위해 IPython 프로파일에 추가해서 사용
In [60]:
def set_trace():
    from IPython.core.debugger import Pdb
    Pdb(color_scheme='Linux').set_trace(sys._getframe().f_back)
    
def debug(f, *args, **kwargs):
    from IPython.core.debugger import Pdb
    pdb = Pdb(color_scheme='Linux')
    return pdb.runcall(f, *args, **kwargs)
  • 첫번째 함수인 set_trace는 매우 간단
  • set_trace()를 코드의 아무 곳에나 넣으면 그 곳에서 실행을 멈추고 살펴볼 수 있다.(예외가 발생하기 바로 전 위치라면 적절하다.)

책에 있는 예제 실습 코드

  • 먼저 ipython_bug2.py로 저장한다.
  • notebook에서 바로 파일로 저장할 수 있는게 정말 좋은 것 같다.
In [61]:
%%writefile ch03/ipython_bug2.py
import sys

def set_trace():
    from IPython.core.debugger import Pdb
    Pdb(color_scheme='Linux').set_trace(sys._getframe().f_back)

def works_fine():
    a = 5
    b = 6
    assert(a + b == 11)

def throws_an_exception():
    a = 5
    b = 6
    assert(a + b == 10)

def calling_things():
    works_fine()
    set_trace()
    throws_an_exception()

calling_things()
Overwriting ch03/ipython_bug2.py
In [62]:
# run ch03/ipython_bug2.py
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug2.py(20)calling_things()
     19     set_trace()
---> 20     throws_an_exception()
     21 
  • c를 누르면 아무런 문제 없이 정상적으로 코드 실행이 재개된다.
ipdb> c
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug2.py in <module>()
     20     throws_an_exception()
     21 
---> 22 calling_things()

/Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug2.py in calling_things()
     18     works_fine()
     19     set_trace()
---> 20     throws_an_exception()
     21 
     22 calling_things()

/Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug2.py in throws_an_exception()
     13     a = 5
     14     b = 6
---> 15     assert(a + b == 10)
     16 
     17 def calling_things():

AssertionError: 
  • debug 함수는 어떤 함수 호출에서라도 대화형 디버거를 쉽게 실행할 수 있도록 해주는데 다음과 같은 함수를 작성했다고 가정
In [1]:
def f(x, y, z=1):
    tmp = x + y
    return tmp / z
  • 이 함수의 내부 로직을 살펴보자.
  • 보통 f 함수는 f(1, 2, z=3) 같은 식으로 호출하게 될 것이다.
  • f 함수의 내부로 진입하기 위해 f를 debug 함수의 첫번째 인자로 넘기고 그 다음에 f 함수의 인자를 차례대로 써 넣으면 된다.
In [2]:
debug(f, 1, 2, z=3)
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.
---------------------------------------------------------------------------
SyntaxError                               Traceback (most recent call last)
SyntaxError: invalid syntax (<string>, line 4)
> (2)f() 1 def f(x, y, z=1): ----> 2 tmp = x + y 3 return tmp / z ipdb> c Out[127]:
  • debug에 디버깅하고 싶은 함수만 넘겨주면 그 함수를 바로 디버깅 할 수 있다.
  • 이 2가지의 짧은 코드가 매일 시간을 절약해 주고 있다.

%run 명령어와 결합하여 사용하는 방법

  • %run -d를 이용해서 실행하면 바로 디버거가 실행
  • 브레이크포인트를 설정하고 스크립트 시작
In [65]:
# %run -d ch03/ipython_bug.py
  • b에 행 번호를 붙여서 넘기면 디버거가 실행되면서 브레이크포인트를 미리 설정해준다.
In [66]:
# %run -d -b3 ch03/ipython_bug.py
Breakpoint 1 at /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py:3
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(1)<module>()
----> 1 def works_fine():
      2     a = 5
1     3     b = 6

ipdb> c
> /Users/re4lfl0w/Documents/SPSE/ch03/ipython_bug.py(3)works_fine()
      2     a = 5
1---> 3     b = 6
      4     assert(a + b == 11)

파이썬 상호대화 디버깅 연습

In [67]:
%%writefile ch03/test_debugger.py
import pdb

def test_debugger(some_int):
    print 'start some_int>>>', some_int
    return_int = 10 / some_int
    print 'end some_int>>', some_int
    return return_int

if __name__ == '__main__':
    pdb.run('test_debugger(0)')
Overwriting ch03/test_debugger.py
In [3]:
%run ch03/test_debugger.py
> <string>(1)<module>()
(Pdb) n
start some_int>>> 0
ZeroDivisionError: 'integer division or modulo by zero'
> <string>(1)<module>()
(Pdb) n
--Return--
> <string>(1)<module>()->None
(Pdb) n
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
/Users/re4lfl0w/Documents/ipython/books/python_data_analysis/ch03/test_debugger.py in <module>()
      8 
      9 if __name__ == '__main__':
---> 10     pdb.run('test_debugger(0)')

/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/pdb.pyc in run(statement, globals, locals)
   1236 
   1237 def run(statement, globals=None, locals=None):
-> 1238     Pdb().run(statement, globals, locals)
   1239 
   1240 def runeval(expression, globals=None, locals=None):

/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/bdb.pyc in run(self, cmd, globals, locals)
    398             cmd = cmd+'\n'
    399         try:
--> 400             exec cmd in globals, locals
    401         except BdbQuit:
    402             pass

<string> in <module>()

/Users/re4lfl0w/Documents/ipython/books/python_data_analysis/ch03/test_debugger.py in test_debugger(some_int)
      3 def test_debugger(some_int):
      4     print 'start some_int>>>', some_int
----> 5     return_int = 10 / some_int
      6     print 'end some_int>>', some_int
      7     return return_int

ZeroDivisionError: integer division or modulo by zero
In [69]:
%%writefile ch03/buggy.py
def divide_one_by(divisor):
    return 1/ divisor

if __name__ == '__main__':
    divide_one_by(0)
Overwriting ch03/buggy.py
In [17]:
# %run ch03/buggy.py
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/buggy.py in <module>()
      3 
      4 if __name__ == '__main__':
----> 5     divide_one_by(0)

/Users/re4lfl0w/Documents/ipython/python_data_analysis/ch03/buggy.py in divide_one_by(divisor)
      1 def divide_one_by(divisor):
----> 2     return 1/ divisor
      3 
      4 if __name__ == '__main__':
      5     divide_one_by(0)

ZeroDivisionError: integer division or modulo by zero

3.4.2 코드 시간 측정: %time과 %timeit

  • 대규모 분석 작업이나 오랜 시간이 소요되는 데이터 분석 애플리케이션에서는 컴포넌트나 함수 호출 혹은 단일 명령어의 실행 시간이 얼마인지 측정해야 하는 경우 존재
  • 전체 처리 과정에서 어떤 함수가 가장 오랫동안 실행되었는지 보고서가 필요
  • 내장 time 모듈을 이용해서 time.clock, time.time 함수로 직접 시간 측정 코드를 작성하는 일은 재미 없는 코드를 반복적으로 써넣어야 하기 때문에 지루
In [71]:
iterations = 10
In [72]:
import time
start = time.time()
for i in range(iterations):
    elapsed_per = (time.time() - start) / iterations
  • 이 코드는 시간을 측정하기 위해 아주 흔하다.
  • IPython 에는 %time과 %timeit 이라는 매직 함수가 있어 간단히 쓸 수 있다.
  • %time: 한 문장을 실행하고 소요된 전체 실행 시간 보여줌
In [73]:
# a very large list of strings
strings = ['foo', 'foobar', 'baz', 'qux',
           'python', 'Guido Van Rossum', 'scari'] * 100000

method1 = [x for x in strings if x.startswith('foo')]
method2 = [x for x in strings if x[:3] == 'foo']
In [74]:
%time method1 = [x for x in strings if x.startswith('foo')]
CPU times: user 219 ms, sys: 11 ms, total: 230 ms
Wall time: 224 ms
In [75]:
%time method2 = [x for x in strings if x[:3] == 'foo']
CPU times: user 95.6 ms, sys: 4.6 ms, total: 100 ms
Wall time: 98.1 ms
  • Wall time값 중요하다.
  • 좀 더 정확한 측정을 하기 위해서는 %timeit 매직 함수 사용. 임의의 한 문장을 여러 번 실행해보고 상당히 정확한 평균 실행 시간을 구해준다.
In [76]:
%timeit method1 = [x for x in strings if x.startswith('foo')]
10 loops, best of 3: 154 ms per loop
In [77]:
%timeit method2 = [x for x in strings if x[:3] == 'foo']
10 loops, best of 3: 60 ms per loop
  • 앞의 예제는 밀리초 단위였지만 대규모 데이터 분석 애플리케이션에서는 더 큰 단위 등장

  • timeit은 마이크로초(1의 -6승) 혹은 나노초(1의 -9승) 단위의 매우 짧은 실행 시간을 가지는 함수나 문장을 분석하는 데 매우 유용

  • 아주 짧은 시간처럼 보이겠지만 20마이크로초가 걸리는 함수를 백만 번 실행하면 5마이크로초가 걸리는 함수보다 15초나 더 걸린다.
  • %time, %timeit 이 굉장히 편한 함수다. 시간을 측정하려면 start, end 구해야 되는데 귀찮은 작업을 모두 한 번에 날려준다. 왜 예전에는 이런 생각을 못했을까?
  • 또 궁금한 것은 %timeit 매직 함수를 어떻게 구현했을까? 이것도 함수를 감싸는 데코레이터의 일종으로 생각은 되는데 자세한 것은 코드를 한 번 봐야할듯 싶다. 어쨌든 매우 편리한 함수!

데코레이터, 폼 입력 필터링을 활용한 SQL Injection 방지 생각

  • 데코레이터의 일종으로 만들면 SQL Injection을 방어하는데 매우 편리하게 사용할 수 있을것 같다.
  • 미리 정의된 string 치환 함수를 데코레이터로 만들어놓고 이 데코레이터로 변환할 함수 앞에 데코레이터만 지정해주면 자동으로 변수의 SQL Injection 취약점이 없어지니.. 근데 이것도 귀찮을 수가 있다. 만약에 데코레이터 추가하는 것을 깜빡하면 어떻게 할 것인가? 예전에 구글앱엔진 개발할때 잠깐 살펴봤던 라이브러리인데 폼으로 입력된 변수값들은 모두 필터링해서 줬던거 같은데.. 이 라이브러리 같은 경우는 아예 폼으로 받는 입력값을 필터링해서 넘겨주니 원천적으로 프레임워크 단에서 차단해줄 것 같다.

Type:        Magic function
String form: <bound method ExecutionMagics.timeit of <IPython.core.magics.execution.ExecutionMagics object at 0x101cb37d0>>
Namespace:   IPython internal
File:        /Library/Python/2.7/site-packages/IPython/core/magics/execution.py
Definition:  %timeit(self, line='', cell=None)
Source:
    @skip_doctest
    @line_cell_magic
    def timeit(self, line='', cell=None):
        """Time execution of a Python statement or expression

    Usage, in line mode:
      %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement
    or in cell mode:
      %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code
      code
      code...

    Time execution of a Python statement or expression using the timeit
    module.  This function can be used both as a line and cell magic:

    - In line mode you can time a single-line statement (though multiple
      ones can be chained with using semicolons).

    - In cell mode, the statement in the first line is used as setup code
      (executed but not timed) and the body of the cell is timed.  The cell
      body has access to any variables created in the setup code.

    Options:
    -n<N>: execute the given statement <N> times in a loop. If this value
    is not given, a fitting value is chosen.

    -r<R>: repeat the loop iteration <R> times and take the best result.
    Default: 3

    -t: use time.time to measure the time, which is the default on Unix.
    This function measures wall time.

    -c: use time.clock to measure the time, which is the default on
    Windows and measures wall time. On Unix, resource.getrusage is used
    instead and returns the CPU user time.

    -p<P>: use a precision of <P> digits to display the timing result.
    Default: 3

    -q: Quiet, do not print result.

    -o: return a TimeitResult that can be stored in a variable to inspect
        the result in more details.


    Examples
    --------
    ::

      In [1]: %timeit pass
      10000000 loops, best of 3: 53.3 ns per loop

      In [2]: u = None

      In [3]: %timeit u is None
      10000000 loops, best of 3: 184 ns per loop

      In [4]: %timeit -r 4 u == None
      1000000 loops, best of 4: 242 ns per loop

      In [5]: import time

      In [6]: %timeit -n1 time.sleep(2)
      1 loops, best of 3: 2 s per loop


    The times reported by %timeit will be slightly higher than those
    reported by the timeit.py script when variables are accessed. This is
    due to the fact that %timeit executes the statement in the namespace
    of the shell, compared with timeit.py, which uses a single setup
    statement to import function or create variables. Generally, the bias
    does not matter as long as results from timeit.py are not mixed with
    those from %timeit."""

    import timeit

    opts, stmt = self.parse_options(line,'n:r:tcp:qo',
                                    posix=False, strict=False)
    if stmt == "" and cell is None:
        return

    timefunc = timeit.default_timer
    number = int(getattr(opts, "n", 0))
    repeat = int(getattr(opts, "r", timeit.default_repeat))
    precision = int(getattr(opts, "p", 3))
    quiet = 'q' in opts
    return_result = 'o' in opts
    if hasattr(opts, "t"):
        timefunc = time.time
    if hasattr(opts, "c"):
        timefunc = clock

    timer = timeit.Timer(timer=timefunc)
    # this code has tight coupling to the inner workings of timeit.Timer,
    # but is there a better way to achieve that the code stmt has access
    # to the shell namespace?
    transform  = self.shell.input_splitter.transform_cell

    if cell is None:
        # called as line magic
        ast_setup = ast.parse("pass")
        ast_stmt = ast.parse(transform(stmt))
    else:
        ast_setup = ast.parse(transform(stmt))
        ast_stmt = ast.parse(transform(cell))

    ast_setup = self.shell.transform_ast(ast_setup)
    ast_stmt = self.shell.transform_ast(ast_stmt)

    # This codestring is taken from timeit.template - we fill it in as an
    # AST, so that we can apply our AST transformations to the user code
    # without affecting the timing code.
    timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
                                    '    setup\n'
                                    '    _t0 = _timer()\n'
                                    '    for _i in _it:\n'
                                    '        stmt\n'
                                    '    _t1 = _timer()\n'
                                    '    return _t1 - _t0\n')

    timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
    timeit_ast = ast.fix_missing_locations(timeit_ast)

    # Track compilation time so it can be reported if too long
    # Minimum time above which compilation time will be reported
    tc_min = 0.1

    t0 = clock()
    code = compile(timeit_ast, "<magic-timeit>", "exec")
    tc = clock()-t0

    ns = {}
    exec(code, self.shell.user_ns, ns)
    timer.inner = ns["inner"]

    if number == 0:
        # determine number so that 0.2 <= total time < 2.0
        number = 1
        for _ in range(1, 10):
            if timer.timeit(number) >= 0.2:
                break
            number *= 10
    all_runs = timer.repeat(repeat, number)
    best = min(all_runs) / number
    if not quiet :
        print(u"%d loops, best of %d: %s per loop" % (number, repeat,
                                                          _format_time(best, precision)))
        if tc > tc_min:
            print("Compiler time: %.2f s" % tc)
    if return_result:
        return TimeitResult(number, repeat, best, all_runs, tc, precision)
In [78]:
x = 'foobar'
In [79]:
y = 'foo'
In [80]:
%timeit x.startswith(y)
1000000 loops, best of 3: 217 ns per loop
In [81]:
%timeit x[:3] == y
10000000 loops, best of 3: 119 ns per loop
  • %timeit은 몇 번 loops를 돌았는지 서로 비교해 보면서 봐야 한다. loops 단위가 다를 수 있다.

3.4.3 기본적인 프로파일링: %prun과 %run -p

  • 코드 프로파일링은 코드 실행 시간 측정과 밀접한 관계
  • 프로파일링은 그 시간이 어디서 소요되었는지 츠겆ㅇ
  • 주로 사용되는 파이썬 프로파일링 도구는 cProfile 모듈인데 IPython에 국한된 도구는 아님
  • cProfile은 프로그램이나 임의의 코드 블록을 실행하면서 각 함수에서 소모된 시간을 계속 기록
  • 명령행에서 cProfile을 사용하는 일반적인 방법은 프로그램 전체를 실행하고 각 함수별로 수집된 시간 정보를 출력하는 방식
In [82]:
%%writefile ch03/cprof_example.py

import numpy as np
from numpy.linalg import eigvals

def run_experiment(niter=100):
    K = 100
    results = []
    for _ in xrange(niter):
        mat = np.random.randn(K, K)
        max_eigenvalue = np.abs(eigvals(mat)).max()
        results.append(max_eigenvalue)
    return results
some_results = run_experiment()
print 'Largest one we saw: %s' % np.max(some_results)
Overwriting ch03/cprof_example.py
In [83]:
def run_experiment(niter=100):
    K = 100
    results = []
    for _ in xrange(niter):
        mat = np.random.randn(K, K)
        max_eigenvalue = np.abs(eigvals(mat)).max()
        results.append(max_eigenvalue)
    return results
In [84]:
!python -m cProfile -s cumulative ch03/cprof_example.py
Largest one we saw: 11.7134449573
         14452 function calls (14364 primitive calls) in 0.866 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    0.868    0.868 cprof_example.py:2(<module>)
        1    0.002    0.002    0.715    0.715 cprof_example.py:5(run_experiment)
      100    0.661    0.007    0.673    0.007 linalg.py:819(eigvals)
        1    0.015    0.015    0.152    0.152 __init__.py:106(<module>)
        3    0.026    0.009    0.128    0.043 __init__.py:1(<module>)
        1    0.001    0.001    0.086    0.086 add_newdocs.py:10(<module>)
        1    0.001    0.001    0.050    0.050 type_check.py:3(<module>)
      100    0.039    0.000    0.039    0.000 {method 'randn' of 'mtrand.RandomState' objects}
        1    0.005    0.005    0.031    0.031 __init__.py:15(<module>)
        1    0.002    0.002    0.017    0.017 __init__.py:7(<module>)
        1    0.007    0.007    0.015    0.015 numeric.py:1(<module>)
        1    0.003    0.003    0.014    0.014 __init__.py:45(<module>)
      275    0.006    0.000    0.008    0.000 function_base.py:2940(add_newdoc)
        1    0.006    0.006    0.007    0.007 __init__.py:88(<module>)
        1    0.005    0.005    0.006    0.006 __init__.py:38(<module>)
        1    0.004    0.004    0.006    0.006 npyio.py:1(<module>)
        1    0.002    0.002    0.006    0.006 index_tricks.py:1(<module>)
        1    0.005    0.005    0.005    0.005 polynomial.py:55(<module>)
        1    0.003    0.003    0.005    0.005 collections.py:1(<module>)
        1    0.004    0.004    0.005    0.005 legendre.py:83(<module>)
        1    0.002    0.002    0.005    0.005 ctypeslib.py:51(<module>)
        1    0.004    0.004    0.004    0.004 chebyshev.py:87(<module>)
        1    0.003    0.003    0.004    0.004 case.py:1(<module>)
      301    0.004    0.000    0.004    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      100    0.003    0.000    0.004    0.000 linalg.py:214(_assertFinite)
        1    0.003    0.003    0.004    0.004 laguerre.py:59(<module>)
      200    0.000    0.000    0.004    0.000 {method 'all' of 'numpy.ndarray' objects}
        1    0.003    0.003    0.004    0.004 hermite.py:59(<module>)
        6    0.000    0.000    0.004    0.001 string.py:148(substitute)
        1    0.003    0.003    0.004    0.004 hermite_e.py:59(<module>)
        6    0.003    0.000    0.004    0.001 {method 'sub' of '_sre.SRE_Pattern' objects}
      100    0.000    0.000    0.003    0.000 fromnumeric.py:1837(all)
      200    0.000    0.000    0.003    0.000 _methods.py:35(_all)
        1    0.000    0.000    0.003    0.003 polynomial.py:4(<module>)
       13    0.000    0.000    0.003    0.000 re.py:188(compile)
       13    0.000    0.000    0.003    0.000 re.py:226(_compile)
        1    0.003    0.003    0.003    0.003 __init__.py:4(<module>)
        1    0.000    0.000    0.003    0.003 _internal.py:6(<module>)
       11    0.000    0.000    0.003    0.000 sre_compile.py:496(compile)
        1    0.002    0.002    0.003    0.003 result.py:1(<module>)
        2    0.002    0.001    0.002    0.001 function_base.py:1(<module>)
        1    0.001    0.001    0.002    0.002 __init__.py:44(<module>)
        1    0.001    0.001    0.002    0.002 main.py:1(<module>)
        1    0.001    0.001    0.002    0.002 _datasource.py:33(<module>)
        1    0.002    0.002    0.002    0.002 arrayprint.py:5(<module>)
        1    0.002    0.002    0.002    0.002 heapq.py:31(<module>)
        1    0.002    0.002    0.002    0.002 runner.py:1(<module>)
        1    0.000    0.000    0.002    0.002 numerictypes.py:82(<module>)
        1    0.001    0.001    0.002    0.002 core.py:21(<module>)
        2    0.001    0.001    0.002    0.001 collections.py:282(namedtuple)
       11    0.000    0.000    0.001    0.000 sre_parse.py:676(parse)
       11    0.000    0.000    0.001    0.000 sre_compile.py:481(_code)
    17/11    0.000    0.000    0.001    0.000 sre_parse.py:302(_parse_sub)
      100    0.000    0.000    0.001    0.000 {method 'max' of 'numpy.ndarray' objects}
        1    0.001    0.001    0.001    0.001 linalg.py:10(<module>)
    17/11    0.001    0.000    0.001    0.000 sre_parse.py:380(_parse)
        1    0.001    0.001    0.001    0.001 __init__.py:10(<module>)
      101    0.000    0.000    0.001    0.000 _methods.py:15(_amax)
        1    0.000    0.000    0.001    0.001 difflib.py:29(<module>)
        1    0.001    0.001    0.001    0.001 __init__.py:3(<module>)
    38/11    0.000    0.000    0.001    0.000 sre_compile.py:33(_compile)
        1    0.001    0.001    0.001    0.001 shutil.py:5(<module>)
        1    0.001    0.001    0.001    0.001 loader.py:1(<module>)
      100    0.001    0.000    0.001    0.000 linalg.py:139(_commonType)
      300    0.000    0.000    0.001    0.000 linalg.py:111(isComplexType)
      100    0.000    0.000    0.001    0.000 numeric.py:462(asanyarray)
        1    0.000    0.000    0.001    0.001 util.py:1(<module>)
        3    0.000    0.000    0.001    0.000 warnings.py:45(filterwarnings)
      238    0.001    0.000    0.001    0.000 {numpy.core.multiarray.array}
        1    0.001    0.001    0.001    0.001 utils.py:4(<module>)
      100    0.001    0.000    0.001    0.000 {method 'astype' of 'numpy.ndarray' objects}
        1    0.001    0.001    0.001    0.001 fromnumeric.py:3(<module>)
      666    0.001    0.000    0.001    0.000 string.py:158(convert)
       40    0.000    0.000    0.001    0.000 core.py:109(get_object_signature)
      100    0.000    0.000    0.001    0.000 linalg.py:106(_makearray)
       15    0.000    0.000    0.001    0.000 sre_compile.py:179(_compile_charset)
        1    0.000    0.000    0.001    0.001 numerictypes.py:304(_add_aliases)
      429    0.001    0.000    0.001    0.000 {issubclass}
       15    0.000    0.000    0.000    0.000 sre_compile.py:208(_optimize_charset)
       23    0.000    0.000    0.000    0.000 core.py:5983(__init__)
       23    0.000    0.000    0.000    0.000 core.py:5988(getdoc)
        1    0.000    0.000    0.000    0.000 utils.py:1(<module>)
        1    0.000    0.000    0.000    0.000 fftpack.py:32(<module>)
      100    0.000    0.000    0.000    0.000 linalg.py:209(_assertNdSquareness)
      100    0.000    0.000    0.000    0.000 linalg.py:101(get_linalg_error_extobj)
      243    0.000    0.000    0.000    0.000 sre_parse.py:202(get)
       11    0.000    0.000    0.000    0.000 sre_compile.py:362(_compile_info)
      285    0.000    0.000    0.000    0.000 sre_parse.py:183(__next)
      932    0.000    0.000    0.000    0.000 {isinstance}
      509    0.000    0.000    0.000    0.000 {getattr}
        1    0.000    0.000    0.000    0.000 defmatrix.py:1(<module>)
        1    0.000    0.000    0.000    0.000 extras.py:10(<module>)
       40    0.000    0.000    0.000    0.000 _inspect.py:120(getargspec)
      100    0.000    0.000    0.000    0.000 numeric.py:392(asarray)
      100    0.000    0.000    0.000    0.000 linalg.py:219(_assertNoEmpty2d)
       26    0.000    0.000    0.000    0.000 numerictypes.py:232(bitname)
    59/32    0.000    0.000    0.000    0.000 sre_parse.py:141(getwidth)
       31    0.000    0.000    0.000    0.000 _inspect.py:159(formatargspec)
        1    0.000    0.000    0.000    0.000 _iotools.py:3(<module>)
       19    0.000    0.000    0.000    0.000 nosetester.py:140(__init__)
        3    0.000    0.000    0.000    0.000 numeric.py:346(extend_all)
      100    0.000    0.000    0.000    0.000 linalg.py:198(_assertRankAtLeast2)
      272    0.000    0.000    0.000    0.000 {method 'strip' of 'str' objects}
      100    0.000    0.000    0.000    0.000 linalg.py:127(_complexType)
        4    0.000    0.000    0.000    0.000 sre_compile.py:259(_mk_bitmap)
     1429    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 case.py:133(TestCase)
        1    0.000    0.000    0.000    0.000 polyutils.py:33(<module>)
      145    0.000    0.000    0.000    0.000 sre_parse.py:131(__getitem__)
        9    0.000    0.000    0.000    0.000 extras.py:241(__init__)
        9    0.000    0.000    0.000    0.000 extras.py:245(getdoc)
      272    0.000    0.000    0.000    0.000 {numpy.lib._compiled_base.add_docstring}
        8    0.000    0.000    0.000    0.000 core.py:7190(__init__)
       27    0.000    0.000    0.000    0.000 core.py:822(__init__)
       31    0.000    0.000    0.000    0.000 _inspect.py:61(getargs)
        1    0.000    0.000    0.000    0.000 numerictypes.py:458(_set_array_types)
        1    0.000    0.000    0.000    0.000 numerictypes.py:288(_add_types)
        8    0.000    0.000    0.000    0.000 core.py:7195(getdoc)
1031/1009    0.000    0.000    0.000    0.000 {len}
      100    0.000    0.000    0.000    0.000 {max}
        1    0.000    0.000    0.000    0.000 records.py:36(<module>)
        1    0.000    0.000    0.000    0.000 __init__.py:265(_reset_cache)
      906    0.000    0.000    0.000    0.000 {method 'group' of '_sre.SRE_Match' objects}
      100    0.000    0.000    0.000    0.000 linalg.py:124(_realType)
       19    0.000    0.000    0.000    0.000 posixpath.py:127(dirname)
      273    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 defchararray.py:17(<module>)
        1    0.000    0.000    0.000    0.000 <string>:2(<module>)
        1    0.000    0.000    0.000    0.000 signals.py:1(<module>)
        1    0.000    0.000    0.000    0.000 suite.py:1(<module>)
        8    0.000    0.000    0.000    0.000 {all}
      512    0.000    0.000    0.000    0.000 {chr}
       51    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
       46    0.000    0.000    0.000    0.000 collections.py:323(<genexpr>)
        1    0.000    0.000    0.000    0.000 _endian.py:4(<module>)
       30    0.000    0.000    0.000    0.000 numerictypes.py:450(_add_array_type)
        3    0.000    0.000    0.000    0.000 __init__.py:78(CFUNCTYPE)
        1    0.000    0.000    0.000    0.000 numerictypes.py:345(_add_integer_aliases)
        1    0.000    0.000    0.000    0.000 numerictypes.py:787(_construct_lookups)
       21    0.000    0.000    0.000    0.000 numerictypes.py:219(_evalname)
        1    0.000    0.000    0.000    0.000 arrayterator.py:9(<module>)
       18    0.000    0.000    0.000    0.000 core.py:902(__init__)
        1    0.000    0.000    0.000    0.000 memmap.py:1(<module>)
        1    0.000    0.000    0.000    0.000 core.py:2563(MaskedArray)
       14    0.000    0.000    0.000    0.000 __init__.py:147(_check_size)
      112    0.000    0.000    0.000    0.000 sre_parse.py:196(match)
        1    0.000    0.000    0.000    0.000 _import_tools.py:1(<module>)
      115    0.000    0.000    0.000    0.000 sre_parse.py:139(append)
       72    0.000    0.000    0.000    0.000 numerictypes.py:130(english_lower)
        3    0.000    0.000    0.000    0.000 __init__.py:493(PYFUNCTYPE)
        1    0.000    0.000    0.000    0.000 scimath.py:17(<module>)
      176    0.000    0.000    0.000    0.000 {min}
       40    0.000    0.000    0.000    0.000 _inspect.py:15(ismethod)
        1    0.000    0.000    0.000    0.000 getlimits.py:3(<module>)
        1    0.000    0.000    0.000    0.000 nanfunctions.py:20(<module>)
       21    0.000    0.000    0.000    0.000 sre_compile.py:355(_simple)
        1    0.000    0.000    0.000    0.000 machar.py:7(<module>)
       74    0.000    0.000    0.000    0.000 _inspect.py:152(strseq)
        1    0.000    0.000    0.000    0.000 linalg.py:76(_determine_error_states)
       12    0.000    0.000    0.000    0.000 ctypeslib.py:303(prep_simple)
       11    0.000    0.000    0.000    0.000 sre_parse.py:179(__init__)
        2    0.000    0.000    0.000    0.000 shape_base.py:1(<module>)
       14    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
        2    0.000    0.000    0.000    0.000 {_ctypes.POINTER}
        1    0.000    0.000    0.000    0.000 nosetester.py:6(<module>)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:2043(amax)
        1    0.000    0.000    0.000    0.000 numerictypes.py:435(_construct_char_code_lookup)
        1    0.000    0.000    0.000    0.000 numpytest.py:1(<module>)
        1    0.000    0.000    0.000    0.000 result.py:26(TestResult)
       75    0.000    0.000    0.000    0.000 {range}
        2    0.000    0.000    0.000    0.000 utils.py:152(deprecate)
        1    0.000    0.000    0.000    0.000 <string>:7(Laguerre)
        3    0.000    0.000    0.000    0.000 result.py:14(failfast)
       91    0.000    0.000    0.000    0.000 {_sre.getlower}
       85    0.000    0.000    0.000    0.000 sre_parse.py:127(__len__)
        1    0.000    0.000    0.000    0.000 arraypad.py:5(<module>)
       21    0.000    0.000    0.000    0.000 {numpy.core.multiarray.empty}
        1    0.000    0.000    0.000    0.000 stride_tricks.py:7(<module>)
       40    0.000    0.000    0.000    0.000 numerictypes.py:157(english_upper)
        1    0.000    0.000    0.000    0.000 defchararray.py:1668(chararray)
      112    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
       14    0.000    0.000    0.000    0.000 numerictypes.py:184(english_capitalize)
        1    0.000    0.000    0.000    0.000 _methods.py:5(<module>)
        1    0.000    0.000    0.000    0.000 __init__.py:349(__init__)
        1    0.000    0.000    0.000    0.000 numerictypes.py:386(_set_up_aliases)
        6    0.000    0.000    0.000    0.000 core.py:1046(__init__)
        1    0.000    0.000    0.000    0.000 _iotools.py:466(StringConverter)
        1    0.000    0.000    0.000    0.000 format.py:136(<module>)
        1    0.000    0.000    0.000    0.000 <string>:7(Legendre)
        1    0.000    0.000    0.000    0.000 <string>:7(Polynomial)
        8    0.000    0.000    0.000    0.000 collections.py:349(<genexpr>)
      124    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 <string>:7(HermiteE)
       11    0.000    0.000    0.000    0.000 {_sre.compile}
        1    0.000    0.000    0.000    0.000 <string>:7(Hermite)
        2    0.000    0.000    0.000    0.000 utils.py:108(__call__)
        1    0.000    0.000    0.000    0.000 <string>:7(Chebyshev)
       43    0.000    0.000    0.000    0.000 _inspect.py:163(<lambda>)
        3    0.000    0.000    0.000    0.000 functools.py:17(update_wrapper)
       38    0.000    0.000    0.000    0.000 sre_parse.py:91(__init__)
        9    0.000    0.000    0.000    0.000 core.py:2419(__init__)
       11    0.000    0.000    0.000    0.000 sre_parse.py:258(_escape)
        1    0.000    0.000    0.000    0.000 _inspect.py:7(<module>)
       19    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
       22    0.000    0.000    0.000    0.000 sre_compile.py:475(isstring)
        1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
        1    0.000    0.000    0.000    0.000 pprint.py:35(<module>)
       17    0.000    0.000    0.000    0.000 {method 'keys' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 StringIO.py:30(<module>)
        4    0.000    0.000    0.000    0.000 sre_parse.py:217(isname)
       40    0.000    0.000    0.000    0.000 _inspect.py:26(isfunction)
        1    0.000    0.000    0.000    0.000 twodim_base.py:3(<module>)
        1    0.000    0.000    0.000    0.000 financial.py:10(<module>)
       18    0.000    0.000    0.000    0.000 {_struct.calcsize}
      126    0.000    0.000    0.000    0.000 {ord}
       45    0.000    0.000    0.000    0.000 {repr}
        2    0.000    0.000    0.000    0.000 numeric.py:2320(seterr)
        1    0.000    0.000    0.000    0.000 defmatrix.py:206(matrix)
        1    0.000    0.000    0.000    0.000 collections.py:26(OrderedDict)
        2    0.000    0.000    0.000    0.000 getlimits.py:244(__init__)
       15    0.000    0.000    0.000    0.000 sre_compile.py:52(fixup)
        1    0.000    0.000    0.000    0.000 numeric.py:2705(__enter__)
        1    0.000    0.000    0.000    0.000 numeric.py:2716(_setdef)
        1    0.000    0.000    0.000    0.000 polytemplate.py:11(<module>)
        1    0.000    0.000    0.000    0.000 {posix.uname}
        9    0.000    0.000    0.000    0.000 core.py:2425(getdoc)
       21    0.000    0.000    0.000    0.000 {sys._getframe}
        3    0.000    0.000    0.000    0.000 {numpy.core.umath.seterrobj}
       18    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
        2    0.000    0.000    0.000    0.000 numeric.py:1617(set_string_function)
       38    0.000    0.000    0.000    0.000 {method 'isalnum' of 'str' objects}
       19    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
        8    0.000    0.000    0.000    0.000 collections.py:347(<genexpr>)
       31    0.000    0.000    0.000    0.000 _inspect.py:39(iscode)
        1    0.000    0.000    0.000    0.000 py3k.py:4(<module>)
        1    0.000    0.000    0.000    0.000 helper.py:4(<module>)
        1    0.000    0.000    0.000    0.000 arraysetops.py:26(<module>)
        1    0.000    0.000    0.000    0.000 keyword.py:11(<module>)
        1    0.000    0.000    0.000    0.000 decorators.py:15(<module>)
        2    0.000    0.000    0.000    0.000 getlimits.py:269(max)
        1    0.000    0.000    0.000    0.000 _internal.py:196(_getintp_ctype)
        1    0.000    0.000    0.000    0.000 info.py:176(<module>)
        5    0.000    0.000    0.000    0.000 sre_parse.py:73(opengroup)
        9    0.000    0.000    0.000    0.000 {method 'update' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 info.py:147(<module>)
        1    0.000    0.000    0.000    0.000 <string>:1(Mismatch)
       11    0.000    0.000    0.000    0.000 sre_parse.py:68(__init__)
        1    0.000    0.000    0.000    0.000 polynomial.py:930(poly1d)
        1    0.000    0.000    0.000    0.000 <string>:1(Match)
        1    0.000    0.000    0.000    0.000 core.py:5758(__new__)
        2    0.000    0.000    0.000    0.000 {numpy.core.multiarray.set_string_function}
        1    0.000    0.000    0.000    0.000 info.py:84(<module>)
        1    0.000    0.000    0.000    0.000 extras.py:1501(__init__)
       13    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
       50    0.000    0.000    0.000    0.000 {_ctypes.sizeof}
       21    0.000    0.000    0.000    0.000 sre_parse.py:135(__setitem__)
        1    0.000    0.000    0.000    0.000 _iotools.py:246(NameValidator)
        1    0.000    0.000    0.000    0.000 ufunclike.py:5(<module>)
        2    0.000    0.000    0.000    0.000 numeric.py:2416(geterr)
        3    0.000    0.000    0.000    0.000 index_tricks.py:231(__init__)
        1    0.000    0.000    0.000    0.000 copy_reg.py:14(pickle)
        2    0.000    0.000    0.000    0.000 {map}
        5    0.000    0.000    0.000    0.000 sre_parse.py:84(closegroup)
        1    0.000    0.000    0.000    0.000 info.py:83(<module>)
        1    0.000    0.000    0.000    0.000 extras.py:1431(__init__)
        1    0.000    0.000    0.000    0.000 {method 'view' of 'numpy.ndarray' objects}
        1    0.000    0.000    0.000    0.000 getlimits.py:194(iinfo)
        1    0.000    0.000    0.000    0.000 core.py:5752(MaskedConstant)
        1    0.000    0.000    0.000    0.000 numeric.py:2701(__init__)
        1    0.000    0.000    0.000    0.000 numeric.py:2710(__exit__)
        1    0.000    0.000    0.000    0.000 _internal.py:224(_ctypes)
        4    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        1    0.000    0.000    0.000    0.000 npyio.py:90(BagObj)
        1    0.000    0.000    0.000    0.000 _datasource.py:49(_FileOpeners)
        1    0.000    0.000    0.000    0.000 arrayterator.py:21(Arrayterator)
        1    0.000    0.000    0.000    0.000 utils.py:957(SafeEval)
        1    0.000    0.000    0.000    0.000 nosetester.py:88(NoseTester)
        1    0.000    0.000    0.000    0.000 warnings.py:74(simplefilter)
        1    0.000    0.000    0.000    0.000 warnings.py:339(__enter__)
        4    0.000    0.000    0.000    0.000 core.py:92(doc_note)
        1    0.000    0.000    0.000    0.000 records.py:286(recarray)
        1    0.000    0.000    0.000    0.000 _import_tools.py:8(PackageLoader)
        1    0.000    0.000    0.000    0.000 StringIO.py:42(StringIO)
        3    0.000    0.000    0.000    0.000 functools.py:39(wraps)
        1    0.000    0.000    0.000    0.000 info.py:34(<module>)
        1    0.000    0.000    0.000    0.000 index_tricks.py:461(ndenumerate)
        1    0.000    0.000    0.000    0.000 weakref.py:243(__init__)
       24    0.000    0.000    0.000    0.000 sre_parse.py:211(isident)
        1    0.000    0.000    0.000    0.000 npyio.py:133(NpzFile)
        1    0.000    0.000    0.000    0.000 core.py:2241(_MaskedPrintOption)
        1    0.000    0.000    0.000    0.000 fnmatch.py:11(<module>)
       67    0.000    0.000    0.000    0.000 {globals}
        1    0.000    0.000    0.000    0.000 _datasource.py:150(DataSource)
        1    0.000    0.000    0.000    0.000 runner.py:28(TextTestResult)
        3    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        1    0.000    0.000    0.000    0.000 numerictypes.py:772(_typedict)
        1    0.000    0.000    0.000    0.000 {_ctypes.set_conversion_mode}
       38    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
        1    0.000    0.000    0.000    0.000 function_base.py:1405(vectorize)
        1    0.000    0.000    0.000    0.000 suite.py:16(BaseTestSuite)
        7    0.000    0.000    0.000    0.000 case.py:609(_deprecate)
        1    0.000    0.000    0.000    0.000 index_tricks.py:434(__init__)
        1    0.000    0.000    0.000    0.000 warnings.py:318(__init__)
        1    0.000    0.000    0.000    0.000 collections.py:381(Counter)
        1    0.000    0.000    0.000    0.000 UserDict.py:58(get)
        1    0.000    0.000    0.000    0.000 index_tricks.py:456(__init__)
        1    0.000    0.000    0.000    0.000 _datasource.py:497(Repository)
        1    0.000    0.000    0.000    0.000 core.py:5555(mvoid)
        1    0.000    0.000    0.000    0.000 arrayprint.py:715(DatetimeFormat)
        9    0.000    0.000    0.000    0.000 {setattr}
        1    0.000    0.000    0.000    0.000 ctypeslib.py:150(_ndptr)
        1    0.000    0.000    0.000    0.000 _iotools.py:155(LineSplitter)
        1    0.000    0.000    0.000    0.000 case.py:1018(FunctionTestCase)
        1    0.000    0.000    0.000    0.000 difflib.py:46(SequenceMatcher)
        1    0.000    0.000    0.000    0.000 warnings.py:355(__exit__)
        1    0.000    0.000    0.000    0.000 loader.py:38(TestLoader)
        1    0.000    0.000    0.000    0.000 __config__.py:3(<module>)
        1    0.000    0.000    0.000    0.000 difflib.py:1672(HtmlDiff)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        4    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        6    0.000    0.000    0.000    0.000 core.py:769(__init__)
        8    0.000    0.000    0.000    0.000 {method '__contains__' of 'frozenset' objects}
        1    0.000    0.000    0.000    0.000 utils.py:1398(WarningManager)
        1    0.000    0.000    0.000    0.000 core.py:5919(__init__)
        1    0.000    0.000    0.000    0.000 version.py:3(<module>)
        2    0.000    0.000    0.000    0.000 utils.py:103(__init__)
        3    0.000    0.000    0.000    0.000 __init__.py:494(CFunctionType)
        1    0.000    0.000    0.000    0.000 pprint.py:84(PrettyPrinter)
        1    0.000    0.000    0.000    0.000 runner.py:119(TextTestRunner)
        1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
        1    0.000    0.000    0.000    0.000 _datasource.py:72(__init__)
        1    0.000    0.000    0.000    0.000 arrayprint.py:532(FloatFormat)
        7    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 records.py:85(format_parser)
        1    0.000    0.000    0.000    0.000 main.py:63(TestProgram)
        2    0.000    0.000    0.000    0.000 sre_parse.py:214(isdigit)
        6    0.000    0.000    0.000    0.000 {numpy.core.umath.geterrobj}
        2    0.000    0.000    0.000    0.000 index_tricks.py:626(__init__)
        1    0.000    0.000    0.000    0.000 index_tricks.py:213(AxisConcatenator)
        1    0.000    0.000    0.000    0.000 arrayprint.py:658(LongFloatFormat)
        3    0.000    0.000    0.000    0.000 __init__.py:104(CFunctionType)
        1    0.000    0.000    0.000    0.000 {zip}
        1    0.000    0.000    0.000    0.000 records.py:216(record)
        1    0.000    0.000    0.000    0.000 difflib.py:766(Differ)
        4    0.000    0.000    0.000    0.000 core.py:2432(__get__)
        1    0.000    0.000    0.000    0.000 memmap.py:23(memmap)
        1    0.000    0.000    0.000    0.000 {_ctypes.dlopen}
        1    0.000    0.000    0.000    0.000 index_tricks.py:85(nd_grid)
        1    0.000    0.000    0.000    0.000 machar.py:17(MachAr)
        1    0.000    0.000    0.000    0.000 core.py:5931(__init__)
        1    0.000    0.000    0.000    0.000 __init__.py:332(CDLL)
        1    0.000    0.000    0.000    0.000 arrayprint.py:743(TimedeltaFormat)
        1    0.000    0.000    0.000    0.000 polyutils.py:48(RankWarning)
        1    0.000    0.000    0.000    0.000 {numpy.core.multiarray.set_typeDict}
        1    0.000    0.000    0.000    0.000 stride_tricks.py:14(DummyArray)
        1    0.000    0.000    0.000    0.000 getlimits.py:24(finfo)
        1    0.000    0.000    0.000    0.000 core.py:5973(_frommethod)
        1    0.000    0.000    0.000    0.000 suite.py:252(_ErrorHolder)
        1    0.000    0.000    0.000    0.000 arrayprint.py:688(LongComplexFormat)
        1    0.000    0.000    0.000    0.000 case.py:45(_UnexpectedSuccess)
        3    0.000    0.000    0.000    0.000 core.py:784(__init__)
        2    0.000    0.000    0.000    0.000 index_tricks.py:145(__init__)
        1    0.000    0.000    0.000    0.000 numeric.py:2639(errstate)
        1    0.000    0.000    0.000    0.000 utils.py:1366(WarningMessage)
        1    0.000    0.000    0.000    0.000 shutil.py:34(SpecialFileError)
        1    0.000    0.000    0.000    0.000 _iotools.py:438(ConverterError)
        1    0.000    0.000    0.000    0.000 extras.py:224(_fromnxfunction)
        1    0.000    0.000    0.000    0.000 signals.py:9(_InterruptHandler)
        1    0.000    0.000    0.000    0.000 __init__.py:243(c_char_p)
        1    0.000    0.000    0.000    0.000 string.py:131(__init__)
        1    0.000    0.000    0.000    0.000 __init__.py:233(c_byte)
        1    0.000    0.000    0.000    0.000 index_tricks.py:439(CClass)
        1    0.000    0.000    0.000    0.000 nanfunctions.py:31(NanWarning)
        1    0.000    0.000    0.000    0.000 index_tricks.py:510(ndindex)
        2    0.000    0.000    0.000    0.000 {hasattr}
        1    0.000    0.000    0.000    0.000 runner.py:12(_WritelnDecorator)
        1    0.000    0.000    0.000    0.000 core.py:7178(_convert2ma)
        1    0.000    0.000    0.000    0.000 linalg.py:43(LinAlgError)
        1    0.000    0.000    0.000    0.000 __init__.py:428(LibraryLoader)
        1    0.000    0.000    0.000    0.000 case.py:98(_AssertRaisesContext)
        1    0.000    0.000    0.000    0.000 UserDict.py:70(__contains__)
        1    0.000    0.000    0.000    0.000 __init__.py:159(py_object)
        1    0.000    0.000    0.000    0.000 __init__.py:112(ModuleDeprecationWarning)
        1    0.000    0.000    0.000    0.000 case.py:25(SkipTest)
        1    0.000    0.000    0.000    0.000 core.py:883(_MaskedBinaryOperation)
        1    0.000    0.000    0.000    0.000 _iotools.py:445(ConverterLockError)
        2    0.000    0.000    0.000    0.000 utils.py:87(_set_function_name)
        1    0.000    0.000    0.000    0.000 copy_reg.py:27(constructor)
        1    0.000    0.000    0.000    0.000 core.py:782(_DomainGreater)
        1    0.000    0.000    0.000    0.000 suite.py:78(TestSuite)
        7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
        1    0.000    0.000    0.000    0.000 __init__.py:260(c_bool)
        1    0.000    0.000    0.000    0.000 __init__.py:388(PyDLL)
        1    0.000    0.000    0.000    0.000 __init__.py:193(c_uint)
        1    0.000    0.000    0.000    0.000 __init__.py:255(c_void_p)
        1    0.000    0.000    0.000    0.000 index_tricks.py:583(IndexExpression)
        1    0.000    0.000    0.000    0.000 __init__.py:359(_FuncPtr)
        1    0.000    0.000    0.000    0.000 core.py:5917(_minimum_operation)
        1    0.000    0.000    0.000    0.000 __init__.py:238(c_char)
        1    0.000    0.000    0.000    0.000 core.py:767(_DomainSafeDivide)
        1    0.000    0.000    0.000    0.000 _endian.py:49(BigEndianStructure)
        1    0.000    0.000    0.000    0.000 core.py:1026(_DomainedBinaryOperation)
        1    0.000    0.000    0.000    0.000 core.py:2390(_arraymethod)
        1    0.000    0.000    0.000    0.000 numeric.py:49(ComplexWarning)
        1    0.000    0.000    0.000    0.000 arrayprint.py:699(ComplexFormat)
        1    0.000    0.000    0.000    0.000 core.py:729(_DomainCheckInterval)
        1    0.000    0.000    0.000    0.000 shutil.py:31(Error)
        2    0.000    0.000    0.000    0.000 __init__.py:429(__init__)
        1    0.000    0.000    0.000    0.000 core.py:128(MAError)
        1    0.000    0.000    0.000    0.000 _internal.py:217(_missing_ctypes)
        3    0.000    0.000    0.000    0.000 _inspect.py:161(<lambda>)
        1    0.000    0.000    0.000    0.000 suite.py:299(_DebugResult)
        1    0.000    0.000    0.000    0.000 case.py:34(_ExpectedFailure)
        1    0.000    0.000    0.000    0.000 core.py:2460(MaskedIterator)
        1    0.000    0.000    0.000    0.000 core.py:5862(_extrema_operation)
        1    0.000    0.000    0.000    0.000 __init__.py:205(c_longdouble)
        1    0.000    0.000    0.000    0.000 _import_tools.py:336(PackageLoaderDebug)
        1    0.000    0.000    0.000    0.000 core.py:131(MaskError)
        1    0.000    0.000    0.000    0.000 shutil.py:38(ExecError)
        1    0.000    0.000    0.000    0.000 __init__.py:291(c_wchar_p)
        1    0.000    0.000    0.000    0.000 polyutils.py:52(PolyError)
        2    0.000    0.000    0.000    0.000 core.py:796(__init__)
        1    0.000    0.000    0.000    0.000 __init__.py:294(c_wchar)
        1    0.000    0.000    0.000    0.000 utils.py:1581(IgnoreException)
        1    0.000    0.000    0.000    0.000 __init__.py:226(c_ubyte)
        1    0.000    0.000    0.000    0.000 index_tricks.py:340(RClass)
        1    0.000    0.000    0.000    0.000 polynomial.py:21(RankWarning)
        1    0.000    0.000    0.000    0.000 numeric.py:2635(_unspecified)
        1    0.000    0.000    0.000    0.000 polyutils.py:69(PolyBase)
        1    0.000    0.000    0.000    0.000 core.py:805(_MaskedUnaryOperation)
        2    0.000    0.000    0.000    0.000 {method 'clear' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:197(c_float)
        1    0.000    0.000    0.000    0.000 __init__.py:172(c_ushort)
        1    0.000    0.000    0.000    0.000 _iotools.py:452(ConversionWarning)
        8    0.000    0.000    0.000    0.000 {method 'isdigit' of 'str' objects}
        1    0.000    0.000    0.000    0.000 core.py:751(_DomainTan)
        6    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        1    0.000    0.000    0.000    0.000 core.py:2246(__init__)
        3    0.000    0.000    0.000    0.000 core.py:737(__init__)
        1    0.000    0.000    0.000    0.000 _inspect.py:162(<lambda>)
        1    0.000    0.000    0.000    0.000 __init__.py:168(c_short)
        1    0.000    0.000    0.000    0.000 utils.py:92(_Deprecate)
        1    0.000    0.000    0.000    0.000 core.py:757(__init__)
        1    0.000    0.000    0.000    0.000 __init__.py:201(c_double)
        1    0.000    0.000    0.000    0.000 polyutils.py:56(PolyDomainError)
        1    0.000    0.000    0.000    0.000 __init__.py:189(c_int)
        1    0.000    0.000    0.000    0.000 _endian.py:26(_swapped_meta)
        1    0.000    0.000    0.000    0.000 extras.py:1485(mr_class)
        1    0.000    0.000    0.000    0.000 arrayprint.py:638(IntegerFormat)
        1    0.000    0.000    0.000    0.000 __init__.py:180(c_ulong)
        1    0.000    0.000    0.000    0.000 extras.py:1419(MAxisConcatenator)
        5    0.000    0.000    0.000    0.000 {method 'remove' of 'list' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:176(c_long)
        1    0.000    0.000    0.000    0.000 core.py:5761(__array_finalize__)
        1    0.000    0.000    0.000    0.000 core.py:794(_DomainGreaterEqual)
        1    0.000    0.000    0.000    0.000 core.py:5929(_maximum_operation)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


  • 실행해보면 함수 이름 순으로 정렬된 결과 출력
  • 어디에서 가장 많은 시간이 소요되었는지 알기 위해 -s 옵션으로 정렬 순서를 지정해주는 것이 일반적인 방법
  • cumtime 열을 훑어보면서 각 함수 내부에서 얼마나 많은 시간이 소요되었는지 확인
  • 만약에 어떤 함수가 다른 함수를 호출한다고 해도 그 함수에 대한 시간 측정은 멈추지 않는다는 점 기억
  • cProfile은 각 함수의 시작과 끝 시간을 기록하여 시간 측정
  • cProfile은 프로그램을 따로 실행하지 않고도 임의의 코드 블록을 프로그램적으로 프로파일링하기 위해 사용
  • %prun 명령과 %run -p 옵션 제공
  • %prun은 cProfile에 사용하는 명령행 옵션을 그대로 받아서 .py 파일 대신 임의의 파이썬 문장을 프로파일링 해준다.
In [85]:
%prun -l 7 -s cumulative run_experiment()
 

4003 function calls in 0.661 seconds

Ordered by: cumulative time List reduced from 32 to 7 due to restriction <7>

ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.661 0.661 :1() 1 0.002 0.002 0.661 0.661 <ipython-input-13-44dd4ffda22c>:4(run_experiment) 100 0.608 0.006 0.619 0.006 linalg.py:819(eigvals) 100 0.039 0.000 0.039 0.000 {method 'randn' of 'mtrand.RandomState' objects} 100 0.003 0.000 0.004 0.000 linalg.py:214(_assertFinite) 300 0.004 0.000 0.004 0.000 {method 'reduce' of 'numpy.ufunc' objects} 200 0.000 0.000 0.003 0.000 {method 'all' of 'numpy.ndarray' objects}

In [86]:
%run -p -s cumulative ch03/cprof_example.py
Largest one we saw: 12.630078966
 
4072 function calls in 0.674 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.674    0.674 <string>:1(<module>)
        1    0.000    0.000    0.674    0.674 interactiveshell.py:2496(safe_execfile)
        1    0.000    0.000    0.668    0.668 py3compat.py:219(execfile)
        1    0.001    0.001    0.667    0.667 {execfile}
        1    0.000    0.000    0.666    0.666 cprof_example.py:1(<module>)
        1    0.001    0.001    0.666    0.666 cprof_example.py:4(run_experiment)
      100    0.618    0.006    0.627    0.006 linalg.py:819(eigvals)
      100    0.037    0.000    0.037    0.000 {method 'randn' of 'mtrand.RandomState' objects}
        1    0.006    0.006    0.006    0.006 posixpath.py:127(dirname)
      100    0.003    0.000    0.004    0.000 linalg.py:214(_assertFinite)
      301    0.003    0.000    0.003    0.000 {method 'reduce' of 'numpy.ufunc' objects}
      200    0.000    0.000    0.003    0.000 {method 'all' of 'numpy.ndarray' objects}
      200    0.000    0.000    0.003    0.000 _methods.py:35(_all)
      100    0.000    0.000    0.002    0.000 fromnumeric.py:1837(all)
      100    0.000    0.000    0.001    0.000 {method 'max' of 'numpy.ndarray' objects}
      101    0.000    0.000    0.001    0.000 _methods.py:15(_amax)
      100    0.000    0.000    0.001    0.000 linalg.py:139(_commonType)
      100    0.000    0.000    0.001    0.000 linalg.py:106(_makearray)
      300    0.000    0.000    0.001    0.000 linalg.py:111(isComplexType)
      100    0.000    0.000    0.000    0.000 numeric.py:462(asanyarray)
      200    0.000    0.000    0.000    0.000 {numpy.core.multiarray.array}
      100    0.000    0.000    0.000    0.000 {method 'astype' of 'numpy.ndarray' objects}
      400    0.000    0.000    0.000    0.000 {issubclass}
      100    0.000    0.000    0.000    0.000 linalg.py:209(_assertNdSquareness)
      100    0.000    0.000    0.000    0.000 linalg.py:101(get_linalg_error_extobj)
      100    0.000    0.000    0.000    0.000 numeric.py:392(asarray)
      100    0.000    0.000    0.000    0.000 linalg.py:219(_assertNoEmpty2d)
      100    0.000    0.000    0.000    0.000 linalg.py:198(_assertRankAtLeast2)
      100    0.000    0.000    0.000    0.000 linalg.py:127(_complexType)
      100    0.000    0.000    0.000    0.000 linalg.py:124(_realType)
      100    0.000    0.000    0.000    0.000 {max}
      200    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 syspathcontext.py:54(__init__)
        1    0.000    0.000    0.000    0.000 posixpath.py:350(abspath)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:2043(amax)
      100    0.000    0.000    0.000    0.000 {getattr}
        2    0.000    0.000    0.000    0.000 iostream.py:189(write)
      101    0.000    0.000    0.000    0.000 {len}
      106    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 syspathcontext.py:57(__enter__)
        1    0.000    0.000    0.000    0.000 py3compat.py:30(cast_bytes)
        1    0.000    0.000    0.000    0.000 posixpath.py:321(normpath)
      100    0.000    0.000    0.000    0.000 {min}
        1    0.000    0.000    0.000    0.000 {open}
        1    0.000    0.000    0.000    0.000 py3compat.py:20(encode)
        2    0.000    0.000    0.000    0.000 {method 'decode' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {posix.getcwdu}
        2    0.000    0.000    0.000    0.000 {method 'encode' of 'unicode' objects}
        2    0.000    0.000    0.000    0.000 utf_8.py:15(decode)
        2    0.000    0.000    0.000    0.000 iostream.py:99(_check_mp_mode)
        6    0.000    0.000    0.000    0.000 {isinstance}
        2    0.000    0.000    0.000    0.000 {_codecs.utf_8_decode}
        1    0.000    0.000    0.000    0.000 posixpath.py:68(join)
        2    0.000    0.000    0.000    0.000 iostream.py:90(_is_master_process)
        1    0.000    0.000    0.000    0.000 {method 'split' of 'unicode' objects}
        5    0.000    0.000    0.000    0.000 {method 'startswith' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 syspathcontext.py:64(__exit__)
        2    0.000    0.000    0.000    0.000 {method 'write' of '_io.StringIO' objects}
        1    0.000    0.000    0.000    0.000 {method 'rstrip' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        2    0.000    0.000    0.000    0.000 {method 'setdefault' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 posixpath.py:258(expanduser)
        2    0.000    0.000    0.000    0.000 {time.time}
        1    0.000    0.000    0.000    0.000 {sys.getdefaultencoding}
        1    0.000    0.000    0.000    0.000 {method 'rfind' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 {method 'join' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 {method 'endswith' of 'unicode' objects}
        2    0.000    0.000    0.000    0.000 {posix.getpid}
        1    0.000    0.000    0.000    0.000 {method 'remove' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 posixpath.py:59(isabs)
        1    0.000    0.000    0.000    0.000 {sys.getfilesystemencoding}
  • %prun을 사용한 것과 동일한 결과를 얻을 수 있다.(물론 일부 바뀐 것들이 있겠지만 큰 틀에서는 동일하다)

3.4.4 함수의 각 줄마다 프로파일링하기

  • %prun이나 다른 cProfile 기반의 프로파일링 기법을 통해 얻은 정보로는 함수의 실행 시간에 대한 전체 상황을 알기 힘들거나 함수 이름을 기준으로 수집된 결과가 너무 복잡해서 분석 어려움
  • line_profiler(PyPI나 다른 패키지 관리 도구를 이용해 얻음)라는 작은 라이브러리를 사용하면 된다.
  • IPython 확장을 포함해 하나 이상의 함수를 줄마다 프로파일링 할 수 있는 %lprun 매직 함수 제공

line_profiler 삽질기

  • 책에 써 있는대로 해봤지만 도저히 동작하지 않는다.
  • 아무리 설정 파일에 추가하고 lprun을 해보지만 그런 모듈은 없다는 친절한 에러를 뱉어준다.
  • 구글링으로도 몇 시간이나 찾아봤지만 못 찾았기 때문에 여기에서는 스킵하겠다. 혹시 되는 분은 알려주기 바란다. 내가 낭비한 시간이 아까워서라도 한 번 해보고 싶다 ㅠㅠ

3.5 IPython HTML 노트북

  • 노트북에는 코드, 출력 결과, 그림을 쉽게 공유할 수 있는 JSON 파일 형식의 .ipynb 문서 존재
  • 최근 파이썬 컨퍼런스에서는 이 .ipynb 파일을 온라인으로 공유해 발표 후에 다른 사람도 직접 실행해 볼 수 있도록 하고 있지만 국내에서는 많이 사용되고 있지 않다.
  • 노트북 애플리케이션은 명령행에서 실행되는 경량 서버 프로세스로 실행
  • 대화형 컴퓨팅에 편리한 도구일 뿐만 아니라 연구와 교육을 위해 재사용이 가능한 이상적인 매체
  • 이 책의 대부분을 IPython 노트북을 사용해서 작성
$ ipython notebook --pylab
2014-05-09 16:28:43.140 [NotebookApp] Using existing profile dir: u'/Users/re4lfl0w/.ipython/profile_default'
2014-05-09 16:28:43.149 [NotebookApp] Using MathJax from CDN: http://cdn.mathjax.org/mathjax/latest/MathJax.js
2014-05-09 16:28:43.168 [NotebookApp] Serving notebooks from local directory: /Users/re4lfl0w/Documents/SPSE
2014-05-09 16:28:43.168 [NotebookApp] The IPython Notebook is running at: http://127.0.0.1:8888/
2014-05-09 16:28:43.168 [NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
  • 대부분의 환경에서는 기본 웹브라우저가 자동으로 열리면서 노트북의 대시보드를 보여준다.
  • 어떨 때에는 직접 URL 입력
  • 브라우저에 노트북이 나타나면 거기서 새 노트북을 생성하고 살펴보자
  • NotebookCloud을 이용하면 EC2 위에서 노트북을 쉽게 열람

gist를 이용한 방법

페이스북 PythonKorea강남스터디 김정주님 제공

어제 발표자료입니다. 교재와 동일한 내용입니다만 참고로 올려봅니다.
http://nbviewer.ipython.org/gist/haje01/44825afdb0b54ac39150
여쭤보셨던 IPython Notebook을 gist를 통해 nbviewer에 올리는 과정은 다음과 같이 간단합니다:
1. 노트북 파일(.ipynb) 내용을 복사하고 https://gist.github.com로 가서 붙여 넣습니다.
2. 저장후 생성된 URL을 복사하고 http://nbviewer.ipython.org로 가셔서 붙여 넣습니다.

3. 여기에서 생성된 URL로 공유해 주시면 됩니다.
다만, 별도 파일(이미지 등)이 있는 경우가 좀 문제인데, 이때는:
1. http://stackoverflow.com/questions/16425770/how-do-you-upload-images-to-a-gist 을 참고하셔서 이미지 파일을 올립니다.
2. 이때 이미지 파일이 images/라는 폴더에 있었다면, IPython내 노트북의 Markdown셀에서 
![desc](images/myimage.png)
형식으로 기술하셔야 합니다.


3.6 IPython을 사용한 제품 개발을 위한 팁

  • 패러다임 시프트: 개발과 디버깅이 쉽고, 궁극적으로는 인터랙티브하게 사용할 수 있도록 코드를 작성하는 것
  • 코딩 스타일과 더불어 약간의 수정을 필요로 하는 코드 리로딩 같은 절차적인 세부사항 존재
  • 대부분의 사용자는 재사용이 쉽고 가능한 한 큰 어려움 없이 함수나 프로그램의 실행 결과를 확인할 수 있는 방식으로 코드를 구조화하고 싶을 것이다. 나는 독립적으로 실행되는 명령행 애플리케이션만을 위한 코드보다는 IPython 에서 실행되는 것을 염두에 두고 개발된 소프트웨어가 훨씬 더 쉽게 작업할 수 있다.
  • 이는 여러분이 몇 달, 심지어는 몇 년 전에 작성한 코드에서 발생한 오류를 분석해야 할 때 특히 중요한 문제

3.6.1 모듈 의존성 리로딩하기

  • import some_lib을 입력하면 some_lib에 있는 코드가 실행
  • 모든 변수와 함수, 그 안에 import는 새로 생성된 some_lib 모듈의 네임스페이스 안에 저장
  • 그 후에 다시 import some_lib을 입력하면 이미 존재하는 모듈 네임스페이스에 대한 참조를 얻을 수 있다.
  • IPython에서 대화형 코드 개발에 있어 잠재적인 어려움은 변경한 다른 모듈에 대한 의존성을 가지는 스크립트를 %run 으로 실행했을 때 겪게 될 것
import some_lib

x = 5
y = [1,2,3,4]
result = some_lib.get_answer(x, y)
  • %run test_script.py을 실행하고 some_lib.py 파일을 변경한 다음 다시 %run test_script.py를 실행하면 변경되기 전의 some_lib을 참조하게 되는데, 이는 파이썬이 모듈을 한 번만 로딩하기 때문
  • 이런 형태는 자동적으로 코드 변경을 전파해주는 MATALB 같은 다른 데이터 분석 환경과는 다르다.
  • 이런 문제에 대응하기 위한 몇 가지 옵션
  • 첫번째 방법은 파이썬의 내장 reload 함수를 이용하도록 다음 예제처럼 test_script.py를 변경하는 것이다.

MATLAB은 자동적으로 코드 변경 전파

  • 모듈이나 패키지는 한 프로그램 안에서도 다양한 곳에서 import 될 수 있기 때문에 파이썬은 모듈이 import 될 때마다 코드를 실행하지 않고 최초에 모듈이 import 되었을 때 모듈의 코드를 캐시에 담아둔다. 그렇지 않으면 모듈 방식과 훌륭한 코드 구성이 애플리케이션의 효율을 떨어뜨릴 수 있다.
import some_lib
reload(some_lib)

x = 5
y = [1,2,3,4]
result = some_lib.get_answer(x, y)
  • 이렇게 하면 test_script.py를 실행할 때마다 항상 새롭게 some_lib의 복사본을 얻을 수 있다.
  • 당연히 의존성이 깊어질수록 모든 곳에 reload를 사용하도록 추가해야 하는 번거로움이 따른다.
  • 이런 문제 때문에 IPython에서는 dreload(deep reload) 함수 존재
  • dreload는 매직 함수는 아니며 깊은(재귀적으로) 모듈을 리로딩하게 해준다.
  • 만일 import some_lib 이라고 입력하지 않고 dreload(some_lib) 이라고 입력한다면 some_lib과 some_lib에 의존하는 다른 모든 모듈을 새로 읽어오려고 할 것이다.
  • 아쉽지만 모든 경우에 이 방법을 적용할 수는 없다.
  • 하지만 IPython을 재시작해야 하는 번거로움은 해결할 수 있다.

3.6.2 코드 설계 팁

  • 코드 설계에 있어 지름길은 없지만 일을 하면서 도움이 되었던 몇 가지 상위 수준의 원리 소개

관련 있는 객체와 데이터는 유지

from my_functions import g

def f(x, y):
    return g(x + y)

def main():
    x = 6
    y = 7.5
    result = x + y

if __name__ == '__main__':
    main()

이 프로그램의 문제점?

  • 프로그램의 실행이 끝나면 IPython 에서는 main 함수 내부에서 실행되는 코드를 모듈의 글로벌 네임스페이스에서 직접 실행되도록 고치는 것(혹은 모듈이 import가 가능하도록 만들고 싶다면 if __name__ == '__main__': 브록 안에서 실행 되도록 한다)
  • 이렇게 하면 %run으로 프로그램을 실행시켜도 main 함수에서 정의된 모든 변수를 살펴볼 수 있게 된다.
  • 이 예제는 큰 의미가 없어 보이지만 앞으로 IPython으로 대용량 데이터를 다루는 좀 더 복잡한 데이터 분석 문제를 살펴볼 것이므로 이렇게 하는 편이 더 낫다.
In [87]:
%%writefile ch03/test_namespace.py
def f(x, y):
    return g(x + y)

def main():
    x = 6
    y = 7.5
    result = x + y

if __name__ == '__main__':
    main()
Overwriting ch03/test_namespace.py
In [88]:
%run ch03/test_namespace.py
In [89]:
x
Out[89]:
'foobar'
In [90]:
result
Out[90]:
1.4666666666666666

main에서 함수 실행

  • main() 함수에 있던 내용을 __main__ 으로 옮기니 run을 실행한 후에 변수들을 참조할 수 있게 됐다.
In [91]:
%%writefile ch03/test_namespace2.py
def f(x, y):
    return g(x + y)

if __name__ == '__main__':
    x = 6
    y = 7.5
    result = x + y
Overwriting ch03/test_namespace2.py
In [92]:
%run ch03/test_namespace2.py
In [93]:
x
Out[93]:
6
In [94]:
result
Out[94]:
13.5

중첩을 피하자

  • 함수를 테스트하거나 디버깅할 때, 확인해야 할 코드를 보기 위해 몇 겹의 양파 껍질을 벗겨야 할까? '중첩을 피하자'는 Zen of Python(PEP20)에 나오는 구절인데, 대화형 방식을 위한 코드를 개발할 때도 마찬가지로 적용
  • 함수와 클래스를 작성할 때 가능한 한 결합도를 낮추고 모듈화하면 테스트(단위 테스트를 작성한다면), 디버깅이 쉬워질 뿐 아니라 대화형 방식으로 사용하기도 쉬워진다.
  • PEP 20(The Zen of Python) by example

긴 파일에 대한 두려움을 버리자

  • IPython에서 10개의 작고 서로 연관된 파일(각각 100여 줄 이하인)을 사용해서 개발을 진행한다면 한두 개의 긴 파일로 작업할 때보다 더 골치가 아플 것
  • 파일 개수가 적다는 말은 곧 리로드할 모듈이 적다는 의미이고, 이 파일일 저 파일 편집하는 일이 줄어든다.
  • 나는 내부적으로 높은 결합도를 가지는 큰 모듈을 유지하는 것이 훨씬 유용하고 '파이썬스럽다'는 사실을 깨달았다.
  • 해법을 위해 반복하다보면 가끔은 큰 파일을 작은 파일로 쪼개는 것이 자연스럽다는 것을 알게 되기도 한다.
  • 물론 이 주장을 지나치게 받아들여서 괴물처럼 하나짜리 파일에 모든 코드를 다 집어넣으라는 것은 아니다. 큰 코드 기반에 어울리는 합리적이고 직관적인 모듈과 패키지 구조를 찾는 일은 약간의 수고가 필요하지만 제대로 일하려면 무엇보다 중요한 과정
  • 각각의 모듈은 내부적으로 응집해야 하고 각 기능을 위한 클래스와 함수를 찾는 일도 최대한 분명해야 한다.

3.7 IPython 고급 기능

3.7.1 IPython 친화적인 클래스 만들기

  • IPython은 살펴보려는 모든 객체의 문자열 표현을 콘솔 친화적인 모양으로 출력
  • 사전, 리스트, 튜플 많은 객체에 대해 내장 pprint 모듈을 사용하면 멋진 모양으로 출력
  • 하지만 사용자 정의 클래스의 경우에는 직접 원하는 형식의 문자열 출력을 생성해줘야만 한다.
In [95]:
class Message:
    def __init__(self, msg):
        self.msg = msg
In [96]:
x = Message('I have a secret')
In [97]:
# 클래스의 기본 문자열 표현이 그리 보기 좋지 않다.
x
Out[97]:
<__main__.Message instance at 0x10a272f80>
  • IPython은 __repr__ 매직 메서드에서 반환되는 문자열을 받아서(output = repr(obj)) 콘솔로 출력한다. 따라서 이 클래스에 __repr__ 메서드를 추가하여 출력을 좀 더 보기 좋게 해보자.
In [98]:
class Message:
    def __init__(self, msg):
        self.msg = msg
    def __repr__(self):
        return 'Message %s' % self.msg
In [99]:
x = Message('I have a secret')
In [100]:
x
Out[100]:
Message I have a secret

3.7.2 프로파일과 설정

  • 색상, 프롬프트, 줄 간격 등 외형과 IPython 쉘의 동작에 관한 대부분의 항목은 폭넓은 설정 시스템을 통해 변경 가능
  • 색상 스키마 변경
  • 입∙출력 프롬프트 모양 변경, Out 프롬프트와 그 다음 In 프롬프트 사이의 빈 줄 제거
  • 여러 개의 임의의 파이썬 문장 실행. 이 설정을 통해 항상 사용하는 모듈을 import 하거나 IPython을 실행할 때마다 실행되기를 원하는 코드 추가가 가능
  • line_profiler의 매직 함수인 %lprun 같은 IPython 확장 활성화
  • 사용자 매직 함수 정의, 시스템 별칭 정의
  • UNIX: ~/.config/ipython/
  • Windows: %HOME%/.ipython/
  • 디렉토리에 있는 ipython_config.py 파일에 지정
  • 사용자 홈 디렉토리는 시스템에 따라 다르다.
  • 설정은 특정 프로파일에 기반해서 수행
  • 일반적으로 IPython을 실행하면 기본적으로 profiler_defualt 디렉토리에 저장된 default 프로파일을 불러온다.
  • 다행스럽게도 각 설정 항목마다 어떤 내용인지 설명해주는 주석이 달려있기 때문에 독자가 직접 고쳐보도록 숙제로 남겨두겠다.

Mac 사용자 프로파일 파일 위치

  • /Users/re4lfl0w/.ipython/profile_default/ipython_config.py
  • ipython만 설치한 후에는 이 사용자 프로파일이 절대 보이지 않는다. ipython profile create secret_project 처럼 프로파일을 하나 만들어줘야 이 폴더가 생성이 된다. 책의 순서가 약간 잘못되어서 엄청난 삽질 후에 알아냈다.
# Configuration file for ipython.

c = get_config()

#------------------------------------------------------------------------------
# InteractiveShellApp configuration
#------------------------------------------------------------------------------

# A Mixin for applications that start InteractiveShell instances.
# 
# Provides configurables for loading extensions and executing files as part of
# configuring a Shell environment.
# 
# The following methods should be called by the :meth:`initialize` method of the
# subclass:
# 
#   - :meth:`init_path`
#   - :meth:`init_shell` (to be implemented by the subclass)
#   - :meth:`init_gui_pylab`
#   - :meth:`init_extensions`
#   - :meth:`init_code`

# Execute the given command string.
# c.InteractiveShellApp.code_to_run = ''

# lines of code to run at IPython startup.
# c.InteractiveShellApp.exec_lines = []

# Enable GUI event loop integration with any of ('glut', 'gtk', 'gtk3', 'none',
# 'osx', 'pyglet', 'qt', 'qt4', 'tk', 'wx').
# c.InteractiveShellApp.gui = None

# Pre-load matplotlib and numpy for interactive use, selecting a particular
# matplotlib backend and loop integration.
# c.InteractiveShellApp.pylab = None

# Configure matplotlib for interactive use with the default matplotlib backend.
# c.InteractiveShellApp.matplotlib = None

# If true, IPython will populate the user namespace with numpy, pylab, etc. and
# an 'import *' is done from numpy and pylab, when using pylab mode.
# 
# When False, pylab mode should not import any names into the user namespace.
# c.InteractiveShellApp.pylab_import_all = True

# A list of dotted module names of IPython extensions to load.
# c.InteractiveShellApp.extensions = []

# Run the module as a script.
# c.InteractiveShellApp.module_to_run = ''

# dotted module name of an IPython extension to load.
# c.InteractiveShellApp.extra_extension = ''

# List of files to run at IPython startup.
# c.InteractiveShellApp.exec_files = []

# A file to be run
# c.InteractiveShellApp.file_to_run = ''

#------------------------------------------------------------------------------
# TerminalIPythonApp configuration
#------------------------------------------------------------------------------

# TerminalIPythonApp will inherit config from: BaseIPythonApplication,
# Application, InteractiveShellApp

# Pre-load matplotlib and numpy for interactive use, selecting a particular
# matplotlib backend and loop integration.
# c.TerminalIPythonApp.pylab = None

# Create a massive crash report when IPython encounters what may be an internal
# error.  The default is to append a short message to the usual traceback
# c.TerminalIPythonApp.verbose_crash = False

# Run the module as a script.
# c.TerminalIPythonApp.module_to_run = ''

# The date format used by logging formatters for %(asctime)s
# c.TerminalIPythonApp.log_datefmt = '%Y-%m-%d %H:%M:%S'

# Whether to overwrite existing config files when copying
# c.TerminalIPythonApp.overwrite = False

# Execute the given command string.
# c.TerminalIPythonApp.code_to_run = ''

# Set the log level by value or name.
# c.TerminalIPythonApp.log_level = 30

# lines of code to run at IPython startup.
# c.TerminalIPythonApp.exec_lines = []

# Suppress warning messages about legacy config files
# c.TerminalIPythonApp.ignore_old_config = False

# Path to an extra config file to load.
# 
# If specified, load this config file in addition to any other IPython config.
# c.TerminalIPythonApp.extra_config_file = u''

# dotted module name of an IPython extension to load.
# c.TerminalIPythonApp.extra_extension = ''

# A file to be run
# c.TerminalIPythonApp.file_to_run = ''

# The IPython profile to use.
# c.TerminalIPythonApp.profile = u'default'

# Configure matplotlib for interactive use with the default matplotlib backend.
# c.TerminalIPythonApp.matplotlib = None

# If a command or file is given via the command-line, e.g. 'ipython foo.py
# c.TerminalIPythonApp.force_interact = False

# If true, IPython will populate the user namespace with numpy, pylab, etc. and
# an 'import *' is done from numpy and pylab, when using pylab mode.
# 
# When False, pylab mode should not import any names into the user namespace.
# c.TerminalIPythonApp.pylab_import_all = True

# The name of the IPython directory. This directory is used for logging
# configuration (through profiles), history storage, etc. The default is usually
# $HOME/.ipython. This options can also be specified through the environment
# variable IPYTHONDIR.
# c.TerminalIPythonApp.ipython_dir = u'/Users/re4lfl0w/.ipython'

# Whether to display a banner upon starting IPython.
# c.TerminalIPythonApp.display_banner = True

# Whether to install the default config files into the profile dir. If a new
# profile is being created, and IPython contains config files for that profile,
# then they will be staged into the new directory.  Otherwise, default config
# files will be automatically generated.
# c.TerminalIPythonApp.copy_config_files = False

# List of files to run at IPython startup.
# c.TerminalIPythonApp.exec_files = []

# Enable GUI event loop integration with any of ('glut', 'gtk', 'gtk3', 'none',
# 'osx', 'pyglet', 'qt', 'qt4', 'tk', 'wx').
# c.TerminalIPythonApp.gui = None

# A list of dotted module names of IPython extensions to load.
# c.TerminalIPythonApp.extensions = []

# Start IPython quickly by skipping the loading of config files.
# c.TerminalIPythonApp.quick = False

# The Logging format template
# c.TerminalIPythonApp.log_format = '[%(name)s]%(highlevel)s %(message)s'

#------------------------------------------------------------------------------
# TerminalInteractiveShell configuration
#------------------------------------------------------------------------------

# TerminalInteractiveShell will inherit config from: InteractiveShell

# auto editing of files with syntax errors.
# c.TerminalInteractiveShell.autoedit_syntax = False

# Use colors for displaying information about objects. Because this information
# is passed through a pager (like 'less'), and some pagers get confused with
# color codes, this capability can be turned off.
# c.TerminalInteractiveShell.color_info = True

# A list of ast.NodeTransformer subclass instances, which will be applied to
# user input before code is run.
# c.TerminalInteractiveShell.ast_transformers = []

# 
# c.TerminalInteractiveShell.history_length = 10000

# Don't call post-execute functions that have failed in the past.
# c.TerminalInteractiveShell.disable_failing_post_execute = False

# Show rewritten input, e.g. for autocall.
# c.TerminalInteractiveShell.show_rewritten_input = True

# Set the color scheme (NoColor, Linux, or LightBG).
# c.TerminalInteractiveShell.colors = 'LightBG'

# Autoindent IPython code entered interactively.
# c.TerminalInteractiveShell.autoindent = True

# 
# c.TerminalInteractiveShell.separate_in = '\n'

# Deprecated, use PromptManager.in2_template
# c.TerminalInteractiveShell.prompt_in2 = '   .\\D.: '

# 
# c.TerminalInteractiveShell.separate_out = ''

# Deprecated, use PromptManager.in_template
# c.TerminalInteractiveShell.prompt_in1 = 'In [\\#]: '

# Make IPython automatically call any callable object even if you didn't type
# explicit parentheses. For example, 'str 43' becomes 'str(43)' automatically.
# The value can be '0' to disable the feature, '1' for 'smart' autocall, where
# it is not applied if there are no more arguments on the line, and '2' for
# 'full' autocall, where all callable objects are automatically called (even if
# no arguments are present).
# c.TerminalInteractiveShell.autocall = 0

# Number of lines of your screen, used to control printing of very long strings.
# Strings longer than this number of lines will be sent through a pager instead
# of directly printed.  The default value for this is 0, which means IPython
# will auto-detect your screen size every time it needs to print certain
# potentially long strings (this doesn't change the behavior of the 'print'
# keyword, it's only triggered internally). If for some reason this isn't
# working well (it needs curses support), specify it yourself. Otherwise don't
# change the default.
# c.TerminalInteractiveShell.screen_length = 0

# Set the editor used by IPython (default to $EDITOR/vi/notepad).
# c.TerminalInteractiveShell.editor = 'vi'

# Deprecated, use PromptManager.justify
# c.TerminalInteractiveShell.prompts_pad_left = True

# The part of the banner to be printed before the profile
# c.TerminalInteractiveShell.banner1 = 'Python 2.7.2 (default, Oct 11 2012, 20:14:37) \nType "copyright", "credits" or "license" for more information.\n\nIPython 1.1.0 -- An enhanced Interactive Python.\n?         -> Introduction and overview of IPython\'s features.\n%quickref -> Quick reference.\nhelp      -> Python\'s own help system.\nobject?   -> Details about \'object\', use \'object??\' for extra details.\n'

# 
# c.TerminalInteractiveShell.readline_parse_and_bind = ['tab: complete', '"\\C-l": clear-screen', 'set show-all-if-ambiguous on', '"\\C-o": tab-insert', '"\\C-r": reverse-search-history', '"\\C-s": forward-search-history', '"\\C-p": history-search-backward', '"\\C-n": history-search-forward', '"\\e[A": history-search-backward', '"\\e[B": history-search-forward', '"\\C-k": kill-line', '"\\C-u": unix-line-discard']

# The part of the banner to be printed after the profile
# c.TerminalInteractiveShell.banner2 = ''

# 
# c.TerminalInteractiveShell.separate_out2 = ''

# 
# c.TerminalInteractiveShell.wildcards_case_sensitive = True

# 
# c.TerminalInteractiveShell.debug = False

# Set to confirm when you try to exit IPython with an EOF (Control-D in Unix,
# Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a
# direct exit without any confirmation.
# c.TerminalInteractiveShell.confirm_exit = True

# 
# c.TerminalInteractiveShell.ipython_dir = ''

# 
# c.TerminalInteractiveShell.readline_remove_delims = '-/~'

# Start logging to the default log file.
# c.TerminalInteractiveShell.logstart = False

# The name of the logfile to use.
# c.TerminalInteractiveShell.logfile = ''

# The shell program to be used for paging.
# c.TerminalInteractiveShell.pager = 'less'

# Enable magic commands to be called without the leading %.
# c.TerminalInteractiveShell.automagic = True

# Save multi-line entries as one entry in readline history
# c.TerminalInteractiveShell.multiline_history = True

# 
# c.TerminalInteractiveShell.readline_use = True

# Enable deep (recursive) reloading by default. IPython can use the deep_reload
# module which reloads changes in modules recursively (it replaces the reload()
# function, so you don't need to change anything to use it). deep_reload()
# forces a full reload of modules whose code may have changed, which the default
# reload() function does not.  When deep_reload is off, IPython will use the
# normal reload(), but deep_reload will still be available as dreload().
# c.TerminalInteractiveShell.deep_reload = False

# Start logging to the given file in append mode.
# c.TerminalInteractiveShell.logappend = ''

# 
# c.TerminalInteractiveShell.xmode = 'Context'

# 
# c.TerminalInteractiveShell.quiet = False

# Enable auto setting the terminal title.
# c.TerminalInteractiveShell.term_title = False

# 
# c.TerminalInteractiveShell.object_info_string_level = 0

# Deprecated, use PromptManager.out_template
# c.TerminalInteractiveShell.prompt_out = 'Out[\\#]: '

# Set the size of the output cache.  The default is 1000, you can change it
# permanently in your config file.  Setting it to 0 completely disables the
# caching system, and the minimum value accepted is 20 (if you provide a value
# less than 20, it is reset to 0 and a warning is issued).  This limit is
# defined because otherwise you'll spend more time re-flushing a too small cache
# than working
# c.TerminalInteractiveShell.cache_size = 1000

# 'all', 'last', 'last_expr' or 'none', specifying which nodes should be run
# interactively (displaying output from expressions).
# c.TerminalInteractiveShell.ast_node_interactivity = 'last_expr'

# Automatically call the pdb debugger after every exception.
# c.TerminalInteractiveShell.pdb = False

#------------------------------------------------------------------------------
# PromptManager configuration
#------------------------------------------------------------------------------

# This is the primary interface for producing IPython's prompts.

# Output prompt. '\#' will be transformed to the prompt number
# c.PromptManager.out_template = 'Out[\\#]: '

# Continuation prompt.
# c.PromptManager.in2_template = '   .\\D.: '

# If True (default), each prompt will be right-aligned with the preceding one.
# c.PromptManager.justify = True

# Input prompt.  '\#' will be transformed to the prompt number
# c.PromptManager.in_template = 'In [\\#]: '

# 
# c.PromptManager.color_scheme = 'Linux'

#------------------------------------------------------------------------------
# HistoryManager configuration
#------------------------------------------------------------------------------

# A class to organize all history-related functionality in one place.

# HistoryManager will inherit config from: HistoryAccessor

# 
# c.HistoryManager.db_log_output = False

# 
# c.HistoryManager.db_cache_size = 0

# Path to file to use for SQLite history database.
# 
# By default, IPython will put the history database in the IPython profile
# directory.  If you would rather share one history among profiles, you can set
# this value in each, so that they are consistent.
# 
# Due to an issue with fcntl, SQLite is known to misbehave on some NFS mounts.
# If you see IPython hanging, try setting this to something on a local disk,
# e.g::
# 
#     ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
# c.HistoryManager.hist_file = u''

# Options for configuring the SQLite connection
# 
# These options are passed as keyword args to sqlite3.connect when establishing
# database conenctions.
# c.HistoryManager.connection_options = {}

# enable the SQLite history
# 
# set enabled=False to disable the SQLite history, in which case there will be
# no stored history, no SQLite connection, and no background saving thread.
# This may be necessary in some threaded environments where IPython is embedded.
# c.HistoryManager.enabled = True

#------------------------------------------------------------------------------
# ProfileDir configuration
#------------------------------------------------------------------------------

# An object to manage the profile directory and its resources.
# 
# The profile directory is used by all IPython applications, to manage
# configuration, logging and security.
# 
# This object knows how to find, create and manage these directories. This
# should be used by any code that wants to handle profiles.

# Set the profile location directly. This overrides the logic used by the
# `profile` option.
# c.ProfileDir.location = u''

#------------------------------------------------------------------------------
# PlainTextFormatter configuration
#------------------------------------------------------------------------------

# The default pretty-printer.
# 
# This uses :mod:`IPython.lib.pretty` to compute the format data of the object.
# If the object cannot be pretty printed, :func:`repr` is used. See the
# documentation of :mod:`IPython.lib.pretty` for details on how to write pretty
# printers.  Here is a simple example::
# 
#     def dtype_pprinter(obj, p, cycle):
#         if cycle:
#             return p.text('dtype(...)')
#         if hasattr(obj, 'fields'):
#             if obj.fields is None:
#                 p.text(repr(obj))
#             else:
#                 p.begin_group(7, 'dtype([')
#                 for i, field in enumerate(obj.descr):
#                     if i > 0:
#                         p.text(',')
#                         p.breakable()
#                     p.pretty(field)
#                 p.end_group(7, '])')

# PlainTextFormatter will inherit config from: BaseFormatter

# 
# c.PlainTextFormatter.type_printers = {}

# 
# c.PlainTextFormatter.newline = '\n'

# 
# c.PlainTextFormatter.float_precision = ''

# 
# c.PlainTextFormatter.verbose = False

# 
# c.PlainTextFormatter.deferred_printers = {}

# 
# c.PlainTextFormatter.pprint = True

# 
# c.PlainTextFormatter.max_width = 79

# 
# c.PlainTextFormatter.singleton_printers = {}

#------------------------------------------------------------------------------
# IPCompleter configuration
#------------------------------------------------------------------------------

# Extension of the completer class with IPython-specific features

# IPCompleter will inherit config from: Completer

# Instruct the completer to omit private method names
# 
# Specifically, when completing on ``object.<tab>``.
# 
# When 2 [default]: all names that start with '_' will be excluded.
# 
# When 1: all 'magic' names (``__foo__``) will be excluded.
# 
# When 0: nothing will be excluded.
# c.IPCompleter.omit__names = 2

# Whether to merge completion results into a single list
# 
# If False, only the completion results from the first non-empty completer will
# be returned.
# c.IPCompleter.merge_completions = True

# Instruct the completer to use __all__ for the completion
# 
# Specifically, when completing on ``object.<tab>``.
# 
# When True: only those names in obj.__all__ will be included.
# 
# When False [default]: the __all__ attribute is ignored
# c.IPCompleter.limit_to__all__ = False

# Activate greedy completion
# 
# This will enable completion on elements of lists, results of function calls,
# etc., but can be unsafe because the code is actually evaluated on TAB.
# c.IPCompleter.greedy = False

#------------------------------------------------------------------------------
# ScriptMagics configuration
#------------------------------------------------------------------------------

# Magics for talking to scripts
# 
# This defines a base `%%script` cell magic for running a cell with a program in
# a subprocess, and registers a few top-level magics that call %%script with
# common interpreters.

# Extra script cell magics to define
# 
# This generates simple wrappers of `%%script foo` as `%%foo`.
# 
# If you want to add script magics that aren't on your path, specify them in
# script_paths
# c.ScriptMagics.script_magics = []

# Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
# 
# Only necessary for items in script_magics where the default path will not find
# the right interpreter.
# c.ScriptMagics.script_paths = {}

#------------------------------------------------------------------------------
# StoreMagics configuration
#------------------------------------------------------------------------------

# Lightweight persistence for python variables.
# 
# Provides the %store magic.

# If True, any %store-d variables will be automatically restored when IPython
# starts.
# c.StoreMagics.autorestore = False

#c.TerminalIPythonApp.extensions = [
#            'line_profiler'
#            ]

#c.TerminalIPythonApp.extensions = ['line_profiler']

IPython 새로운 프로파일 생성

In [101]:
!ipython profile create secret_project
[ProfileCreate] Generating default config file: u'/Users/re4lfl0w/.ipython/profile_secret_project/ipython_config.py'
[ProfileCreate] Generating default config file: u'/Users/re4lfl0w/.ipython/profile_secret_project/ipython_notebook_config.py'
[ProfileCreate] Generating default config file: u'/Users/re4lfl0w/.ipython/profile_secret_project/ipython_nbconvert_config.py'
$ ipython --profile=secret_project
Python 2.7.5 (v2.7.5:ab05e7dd2788, May 13 2013, 13:18:45) 
Type "copyright", "credits" or "license" for more information.

IPython 1.2.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

IPython profile: secret_project

3.8 감사의 글

  • IPython 개발팀이 작성한 훌륭한 문서에서 많은 부분 인용

IPython 참고 자료

In [4]:
from IPython.display import YouTubeVideo

The IPython Notebook Revolution

  • 이 동영상에 굉장히 재미있는 것들이 많이 소개되어 있습니다. 추천합니다.
In [5]:
YouTubeVideo('t_TzRaK9kpU')
Out[5]: