'파이썬'에 해당되는 글 9건

  1. 2018.04.03 Jupyter Notebook이란?
  2. 2018.04.03 iterator, generator 사용법
  3. 2018.04.03 decorator(wrapper) 사용법
posted by 귀염둥이채원 2018. 4. 3. 01:58

0. 간단 소개

파이썬을 실행할 때 가장 처음 보게 되는 것이 Python REPL(Read-eval-print loop)이다. 터미널에서 python 혹은 python3 라고 입력하면 나오는 파이썬 커널이다. 가볍기도 하고 시커먼 화면이 멋있기도 해서 간단한 코드(주로 타입 확인, 함수 매개변수 테스트)를 써볼 때 쓴다.

Python REPL의 확장 버전이 IPython Notebook이다. repl에선 여러 줄의 코드를 실행하기가 어렵다. 할 수는 있지만 여러 줄의 함수를 선언할 때 한 글자라도 틀리면 다시 처음부터 해야한다. 그래서 여러 줄 단위 실행 및 결과 확인, 편집 유용성 등의 기능이 추가되어 만들어진 것이 IPython Notebook이다. 실행 모습을 보면 감이 바로 팍 올 것이다. 물론 Python 스크립트 파일을 만들어서 커널로 실행하든, 서브라임 텍스트로 빌드해도 된다. 하지만 어떤 문법 공부 혹은 데이터를 다양하게 다루며 테스트를 할 때 굉장히 많은 코드 cell들이 생기게 된다. 1-10라인은 A 테스트, 11-20라인은 B 테스트 등으로 말이다. 이 때 일반 에디터로 작업하면 매 번 다른 코드들은 주석처리를 해서 실행하거나 cell 단위로 여러 파일을 생성해서 각각 실행해야 한다. 솔직히 귀찮다. 한 파일 내에서 쉽게 코드 cell 단위로 실행할 수 있다는 점이 최고 장점이다.

다만 IPython Notebook은 파이썬 전용이다. 그리고 한 번 실행하고 Python 버전을 바꾸려면 로컬 서버를 내렸다가 다시 올려야한다. 이런 문제를 해결한 것이 Jupyter Notebook이다. Jupyter는 쉽게 실행 커널을 바꿀 수 있다. 서버가 돌아가는 상황에서 파이썬의 버전을 바꿀 수도 있고, 아예 다른 언어로도 바꿀 수 있다.(다른 언어 커널 추가하는 것은 nacyot님 블로그에 자세한 설명이 있다.) 그래서 현재는 만약 IPython을 실행한다면 곧 없어질거라며 Jupyter를 쓰라는 메시지를 보게 될 것이다.

1. 설치

pip install jupyter


2. 실행

terminal에서 jupyter notebook이라고 치면 기본 브라우저에서 notebook 창이 새로 뜬다. 오른쪽 위에 new 누르고 Python3 notebook 선택하면 새로운 파일이 열린다. 확장자는 ipynb다.


3. 단축키

h를 누르면 키 도움 팝업이 뜬다. command 모드, edit 모드 별로 대표적인 것 몇 개만 정리한다. 다른 magic 명령어 등을 알아보려면 다음 슬라이드 참고.

  • command mode
    • enter : edit mode로 바꿈. 선택된 cell로.
    • ab : 순서대로 위, 아래에 cell 추가
    • ymr: 순서대로 cell 모드를 code, markdown, raw로 바꾸기
    • 123456 : 선택된 셀을 마크다운으로 바꿔서 heading을 적용한다.
    • jk : 셀 이동. 아래, 위. vim 키랑 똑같다. shift 누른 후에 키를 누르면 여러 개 선택된다.
    • xcshift+vv : cut, copy, 위에 붙여넣기, 아래에 붙여넣기
    • dd: 선택된 cell 삭제. 역시 vim 느낌
    • z: 삭제 취소
    • shift+M : 선택된 cell 합병. 하나만 선택되면 바로 아래꺼랑 합병
    • L : 라인 넘버 생기게 하기
    • shift+spacebarspacebar : 스크롤 업, 다운
  • edit mode
    • ESC: command 모드로 바꿈.
    • enter: 한 뭉치에서 줄 바꿈이다. 에디터에서 편집하는 것처럼 하면 된다.
    • shift+enter or ctrl+enter: 한 뭉치 실행
    • ctrl + shift + - : 커서 위치 아래 부분을 split한다.

4. Magic commands

  • 노트북 자체를 관리할 수 있는 명령어들. % or %% 기호를 앞에 쓰면 된다.

  • timeit

    • %timeit my_func(10) : 해당 함수를 실행하는 시간과 loop 등을 쉽게 알려준다.
    • %%timeit : 셀 전체의 실행시간을 알고싶으면 셀 맨 위에 이렇게 써주면 된다.
  • %matplotlib inline

    • 디폴트는 matplotlib이 그리는 이미지가 독립된 윈도우에 위치한다.
    • 하지만 위처럼 특정 backend를 선택하도록 지정해주면 이미지를 렌더하는 소프트웨어를 선택할 수 있다.
    • 위 예제는 노트북이 바로 이미지를 렌더하는 예다.
  • 레티나 고화질 모니터를 쓴다면 맨 위에 아래처럼 쓰면 선명한 이미지를 얻을 수 있다

    %matplotlib inline
    %config InlineBackend.figure_format = 'retina'
    
  • %pdb : 디버거 사용할 수 있다. 자세한 내용은 https://youtu.be/bZZTeKPRSLQ


5. Export

.ipynb 파일 자체가 거대한 json 파일이다. 그래서 지정하는대로 쉽게 다른 포맷으로 변경할 수 있는데 다음 명령어로 할 수 있다. 더 자세한 내용은 다음 링크에서 확인한다.

jupyter nbconvert --to html notebook.ipynb
jupyter nbconvert --to pdf notebook.ipynb


6. Slideshow

  • 만든 노트북을 쉽게 슬라이드로 만들 수 있다.
  • 메뉴 -> View -> Cell Toolbar -> Slideshow
  • 셀에 원하는 속성을 넣어주면 된다.
    • Slides : 메인 슬라이드 의미. 왼쪽, 오른쪽으로 넘어가는 메인 플로우.
    • Sub-slides : 메인 슬라이드의 하위내용. 위 아래 커서로 이동 가능
    • Fragments : 처음엔 숨겨지지만 버튼을 누르면 등장한다.
    • Skip : 넘기는 셀
    • Notes : speaker notes로 만들 수 있음
  • 슬라이드 파일로 만들기: jupyter nbconvert notebook.ipynb --to slides
  • 브라우저에 띄우기: jupyter nbconvert notebook.ipynb --to slides --post serve


7. Jupyter Server

서버에서 jupyter notebook server를 실행하는 방법

  • 키 파일을 만든다.

    mkdir ~/.ssl
    cd ~/.ssl
    sudo openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout "cert.key" -out "cert.pem" -batch
  • 주피터 서버 비밀번호를 만든다. ipython 실행해서 다음 코드로 비밀번호를 생성하고, sha로 시작하는 해싱 코드를 복사해둔다.

    from IPython.lib import passwd
    passwd()
    # type your password
    exit
  • jupyter config

    • 파일 생성: jupyter notebook --generate-config
    • 파일 편집해서 아래 내용 추가: vi ~/.jupyter/jupyter_notebook_config.py
    c = get_config()
    c.NotebookApp.certfile = u'/home/ubuntu/.ssl/cert.pem'
    c.NotebookApp.keyfile = u'/home/ubuntu/.ssl/cert.key'
    c.IPKernelApp.pylab = 'inline'
    c.NotebookApp.ip = '*'
    c.NotebookApp.open_browser = False
    c.NotebookApp.password = 'sha1:fc216:3a35a98ed980b9...'
    c.NotebookApp.port = 8888
  • tmux로 세션 연 다음에 주피터 노트북 서버를 실행한다.

    • nohup jupyter notebook
    • nohup은 verbose를 출력하지 않고 파일로 기록해주는 역할을 한다.
  • ip와 port를 그대로 치지말고 꼭 https를 맨 앞에 붙여준다. 크롬은 ip 주소를 그대로 치면 http로 접속하기 때문에 ssl error가 날 수 있다


'파이썬' 카테고리의 다른 글

정규표현식(Regular Expression)  (0) 2018.04.09
Python 문법  (0) 2018.04.03
iterator, generator 사용법  (0) 2018.04.03
decorator(wrapper) 사용법  (0) 2018.04.03
파이썬에서 날짜와 시간 다루기  (0) 2018.04.03
posted by 귀염둥이채원 2018. 4. 3. 01:58

1. iterator

iterate 단어 자체의 의미가 '반복하다'이다. 주로 쓰이는 다음 세 가지 단어들에 기본 의미만 적용해도 무엇을 의미하는지 유추할 수 있다.

# iterable
list_ex = [0, 1, 2]

# iterator
iterator_ex = iter(list_ex)

# iteration 동작 python3.x
next(iterator_ex) # 출력: 0
next(iterator_ex) # 출력: 1
next(iterator_ex) # 출력: 2
next(iterator_ex) # StopIteration

# iteration 동작 python2.x
iterator_ex.next()
  • iterable: 반복이 되는 객체다. 일반적으로 생각했을 때 for i in iterable: 형태로 사용할 수 있었던 것들이다. 리스트, 딕셔너리, 세트, 튜플 등등이다.
  • iterator: 반복을 하게 하는 객체다. 위 iterable 객체를 iter()의 매개변수로 넣어서 만든다. iterator를 가지고 iterable을 반복시키는 것이다. iterator 자체를 출력해보면 <list_iterator object at 0x10bc06358>라고 뜬다. 튜플이라면 list 대신 tuple이 된다.
  • iteration: 반복하는 그 동작 자체를 말한다. next()를 실행할 때마다 iterable의 원소 하나하나를 계속 리턴한다. 끝에 도달하면 반복을 멈춘다. 파이썬 버전 2와 버전 3에서 next 실행 방식이 바뀌었다.

2. generator

A. 기본

  • generator는 쉽게 iterator를 생성할 수 있는 수단이다. yield만 쓰면 generator가 된다. yield는 일반함수 쓰듯이 사용하면 되는데 뒤에 괄호는 안 붙는다. next()가 실행될 때마다 yield 가 실행됐던 그 순간으로 되돌아가서 다음 코드를 실행한다. 즉 yield는 마지막 실행했던 그 순간을 기억하고 있는 것이다.
  • 이런 방식이 필요한 이유는 대량의 데이터를 처리할 때 한 번에 모든 것을 처리하는 것은 시간이 오래걸리기 때문에 데이터를 소량씩 중간 중간 처리해야할 때가 있기 때문이다. 소량의 데이터라면 상관 없지만 이렇게 대량일 경우엔 모든 작업이 다 끝나기를 마냥 기다릴 수만은 없다. 물론 그 때 그 때 중간 상황을 변수로 저장해서 통째로 넘길 수도 있지만 이보단 generator를 써서 상태를 유지하고 제어권만 넘기는 방식이 더 유용하다.
def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
for char in reverse('golf'):
    print(char, end=' ')
# 출력: f l o g
######################################
def gen_num():
    i = 0
    while 1:
        yield i
        i += 1
a = gen_num()
for x in range(10) :
    print(a.next())
# 출력: 0 1 2 3 4 5 6 7 8 9

iter()가 고정된 순서(시작부터 마지막까지)의 iterator를 만드는거라면 generator는 내가 순서와 전후 처리에 있어서 마음껏 customize를 할 수 있는 iterator 생성기라고 할 수 있다.

B. 응용

  • 위 예시처럼 함수가 아니라 클래스에서도 __iter__()와 __next__()를 구현해주면 된다.
    • __iter__(): 이 메소드는 클래스 내에서 iterator 객체를 리턴해주면 된다. 즉 원하는 데이터를 선택하고 return iter(data) 하면 된다. 당연한 말이지만 만약 객체의 데이터가 딕셔너리라면 키 데이터를 iter의 매개변수로 넣어주면 된다.
    • __next__(): 제너레이터를 시작하고, 중단 지점에서 다시 재시작하게 해준다. 만약 제너레이터가 다른 값을 yield하지 않고 끝나게될 땐 StopIteration exception을 raise하면 된다. 실제로 yield 코드를 쓰는게 아니라 그냥 return하면 된다. 이 메소드는 for 반복 등에서 내부적으로 자동으로 실행된다.

1) 그냥 next() 구현 예

다음은 그냥 사용자가 만든 next라는 메소드를 호출할 수 있는 예다. y = yrange(3) 으로 객체를 만들고 y.next()를 호출하면 순서대로 0, 1, 2가 나오고 마지막에 Exception이 raise된다. 이 때는 next(y) 하면 오류난다.

class yrange:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()

2) __next__() 구현 예

위 1)에서와 달리 next(counter) 하면 정상적으로 순서대로 출력이 된다.

class Counter(object):
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        'Returns itself as an iterator object'
        return self

    def __next__(self):
        'Returns the next value till current is lower than high'
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

3. 최종 정리

next(something) 형태로 쓸 수 있으려면, 즉 iterator 객체를 만들려면

  • 함수 형태: 내부에서 yield를 쓰면 된다.
  • 클래스: __next__() 메소드가 구현되면 된다.
  • 객체: iter(iterable) 함수의 리턴값을 사용한다.


'파이썬' 카테고리의 다른 글

Python 문법  (0) 2018.04.03
Jupyter Notebook이란?  (0) 2018.04.03
decorator(wrapper) 사용법  (0) 2018.04.03
파이썬에서 날짜와 시간 다루기  (0) 2018.04.03
*args, **kwargs란?  (0) 2018.04.03
posted by 귀염둥이채원 2018. 4. 3. 01:57

1. 배경

  • 파이썬에서 함수는 first citizen이다. 즉 함수를 다른 함수의 매개변수로 전달할 수도 있고, 리턴값으로 사용할 수도 있다. sort 함수에서 key값으로 정렬 기준을 정할 때 람다를 쓰는 것이 한 예다. 어쨌든 이런 특성 덕분에 새로운 용법이 여럿 생겼는데 그 중 하나가 decorator다.
# 함수를 인자로 전달하는 예
def verbose(func):
    print "Begin", func.__name__;
    func();
    print "End", func.__name__;

C도 함수를 포인터를 활용해 매개변수로 전달할 수 있지만 함수 자체를 전달하는 것은 아니다.

2. 사전 요약 정리

  • 타입: 함수 또는 클래스 형태로 존재한다. 디자인 패턴이다.
  • 생김새
    • 함수형: decorator 함수가 함수를 인자로 받고, 다른 내용들로 꾸며서(wrapping), 새로운 함수를 만든 후, 새 함수를 리턴한다.
    • 클래스: 함수를 매개변수로 받는 init 함수를 정의하고, __call__ 함수 내부에서 인스턴스 변수(함수 타입)를 decorate 한다.
  • 의미: 기존 함수의 본래 기능은 건드리지 않는다. 기존 함수 전 후로 진입 루틴, 출구 루틴을 추가한다.
  • 용법: decorator 앞에 @를 붙여서 타겟 함수 코드 def 바로 위에 적어준다.
  • 플로우
    • 함수형: 컴파일러가 @verbose를 만나면 바로 다음 함수를 매개변수로 받아서 decorator인 verbose 함수 실행 -> decorated function을 리턴 -> 타겟 함수가 실행될 때 decorated function이 대신 실행된다.
    • 클래스: 컴파일러가 @Verbose를 만나면 Verbose 클래스의 인스턴스 생성되면서 __init__ 함수 실행 -> 타겟 함수 실행 코드 만남 -> 타겟 함수가 아닌 decorated function이 실행됨

3. decorator 생성, 적용

A. 함수형 데코레이터

decorator를 활용할 때 매 번 타겟 함수를 verbose 함수에 넣어서 바꾼 후 다시 대입해줄 순 없다. 귀찮은 작업일뿐더러 시각적으로 목적이 명확하게 드러나지도 않는다. 그래서 @ 기호를 사용하여 타겟 함수 위에서 decorate 해준다.

def verbose(func):  # 함수를 인자로 받는 verbose decorator
    def new_func(): # 새롭게 만들어지는 함수다.
        print "Begin", func.__name__   # 꾸밈
        func()                         # 인자로 받은 함수
        print "End", func.__name__     # 꾸밈
    return new_func    # 인자로 받은 함수를 꾸민 함수를 만들어 리턴

# 미사용
def my_function():
    print "hello, world."
my_function = verbose(my_function)
my_function()

# decorator 사용
@verbose
def my_function():
    print "hello, world."
my_function()

B. 클래스형 데코레이터

클래스형 데코레이터는 아래처럼 구현해주면 된다. __call__ 함수가 함수형 데코레이터처럼 되는 것이다. 함수형 데코레이터와는 다르게 리턴값은 필요하지 않다.

class Verbose:
    def __init__(self, f):
        print "Initializing Verbose."
        self.func = f;

    def __call__(self):
        print "Begin", self.func.__name__
        self.func();
        print "End", self.func.__name__

@Verbose
def my_function():
    print "hello, world."

print "Program start"
my_function();

# Initializing Verbose.
# Program start
# Begin my_function
# hello, world.
# End my_function

실행 순서

  1. 파이썬 컴파일러가 @Verbose를 만나면 Verbose 클래스의 인스턴스를 만든다. 그래서 __init__ 함수가 실행되고 'Initializing Verbose.'가 출력되는 것이다.
  2. print문 실행한다.
  3. my_function() 코드를 만나면 my_function 그 자체가 아닌 decorated function이 실행된다. 그래서 'Begin~', 'hello~', 'End~'가 차례로 출력되는 것이다. __call__ 함수의 내용처럼.

4. 매개변수를 갖는 타겟 함수

decorated function이나, call 함수에 변수를 추가해주면 된다. 만약 이름 하나를 입력받아서 출력하는 타겟함수라면 decorated function, call 함수의 매개변수로 하나 더 추가해주면 된다. 그 매개변수가 내부에서 타겟함수 실행될 때 매개변수로 들어가주면 되고. 하지만 이렇게 만들면 오로지 매개변수가 하나인 타겟함수만 decorate할 수 있다. 그래서 나온게 *args**kwargs다.

다음처럼 2줄만 수정해주면 된다. __call__ 함수와 call 내부의 타겟함수 실행 부분에 *args**kwargs 추가.

class Verbose:
    def __init__(self, f):
        print "Initializing Verbose."
        self.func = f

    def __call__(self, *args, **kwargs):    #여기 
        print "Begin", self.func.__name__
        self.func(*args, **kwargs)          #여기
        print "End", self.func.__name__

@Verbose
def my_function(name):   #l 인자를 갖는 함수
    print "hello,", name

5. Multiple decorators

@decorator2
@decorator1
def some_method():
    # do stuff
# 위 코드는 아래와 똑같다.
wrapped_method = decorator2(decorator1(some_method))

6. [중요] @wraps 데코레이터 추가 활용

shoveller 님의 설명이다. 정말 중요한 팁이다. 한 마디로 말해서 wraps를 쓰면 디버깅할 때 어디서 오류났는지 정확한 위치를 알 수 있다. wraps가 없이 데코레이터를 쓰면 실행 위치를 원래 함수가 아니라 데코레이터 안의 새롭게 만들어지는 함수로 인식한다. 다음 코드를 통해서 확인

from functools import wraps

def without_wraps(func):
    def __wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return __wrapper
 
def with_wraps(func):
    @wraps(func)
    def __wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return __wrapper
 
@without_wraps
def my_func_a():
    """Here is my_func_a doc string text."""
    pass
 
@with_wraps
def my_func_b():
    """Here is my_func_b doc string text."""
    pass
 
# Below are the results without using @wraps decorator
print my_func_a.__doc__
# >>> None
print my_func_a.__name__
# >>> __wrapper
 
# Below are the results with using @wraps decorator
print my_func_b.__doc__
# >>> Here is my_func_b doc string text.
print my_func_b.__name__
# >>> my_func_b

7. 주 사용 형태

  • Caching: 메모리 최적화 용도로 사용한다.
  • Logging / Debugging: 함수 실행을 로깅하거나, 데코레이터에 매개변수를 넣어서 테스트를 할 수도 있다.
  • Authentication / Authorization: 유저가 접속해 있는지, 그리고 이 유저에게 해당 타겟 메소드에 대한 권한이 있는지 확인
  • Audit trails: 메소드가 호출되는 맥락이나 인스턴스 추적
  • Namespacing: 메소드가 실행될 데이터베이스 네임스페이스나 context를 만들기.

마지막 namespacing은 확실히 이해되진 않지만 아마도 파이썬의 클로져 특징을 활용한다는 의미가 아닐까 싶다.

###A. 사용자 확인

예를 들어 웹 앱을 개발할 때 로그인 상태를 항상 체크해야 할 경우가 많다. 로그인 되어있다면 이렇게 보여주고, 안돼있다면 저렇게 보여주는 형태로 말이다. 이 때 주로 하는 방법은 다음처럼 authenticator를 사용하는 것이다.

def show_page(request):
    authenticator.authenticate(request)
    # do stuff
    return response

하지만 매번 저 코드를 쳐넣는것은 보기도 안좋을 뿐더러 나중에 하나하나 고치기도 힘들다. 그래서 decorator를 사용한다. 다음처럼.

def authenticate(func):
    def authenticate_and_call(*args, **kwargs):
        if not Account.is_authentic(request): 
            raise Exception('Authentication Failed.')
        return func(*args, **kwargs)
    return authenticate_and_call

@authenticate
def show_page(request):
    # do stuff
    return response

B. 권한 확인(meta decorator)

def authorize(role):
    def wrapper(func):
        def authorize_and_call(*args, **kwargs):
            if not current_user.has(role): 
                raise Exception('Unauthorized Access!')
            return func(*args, **kwargs)
        return authorize_and_call
    return wrapper

@authorize('admin')
@authenticate
def show_page(request):
    # do stuff

# 위 코드는 아래 한 줄과 똑같은 의미다.
authorized_show_page = authorize('admin')(authenticate(show_page))

위 코드를 클래스 형태로 만들면 다음과 같다. 클래스가 좀 더 이해하기 쉬움.

class Authorize(object):
    def __init__(self, role):
        self.role = role
    def __call__(self, func):
        def authorize_and_call(*args, **kwargs):
            if not current_user.has(role): 
                raise Exception('Unauthorized Access!')
            return func(*args, **kwargs)
        return authorize_and_call


'파이썬' 카테고리의 다른 글

Jupyter Notebook이란?  (0) 2018.04.03
iterator, generator 사용법  (0) 2018.04.03
파이썬에서 날짜와 시간 다루기  (0) 2018.04.03
*args, **kwargs란?  (0) 2018.04.03
argparse 사용 하기  (0) 2018.04.03