733
파이썬 생존 안내서 이흥섭 @ 왓 스튜디오 2016년 10월 11일 넥슨토크

파이썬 생존 안내서 (자막)

  • Upload
    -

  • View
    8.227

  • Download
    7

Embed Size (px)

Citation preview

Page 1: 파이썬 생존 안내서 (자막)

파이썬 생존 안내서이흥섭 @ 왓 스튜디오2016년 10월 11일 넥슨토크

Page 2: 파이썬 생존 안내서 (자막)

파이썬 생존 안내서이흥섭 @ 왓 스튜디오2016년 10월 11일 넥슨토크

안녕하세요.

Page 3: 파이썬 생존 안내서 (자막)

이흥섭게임서버 아키텍트

@ 왓 스튜디오

파이썬을 주제로 발표하게 된왓 스튜디오의 이흥섭입니다.

Page 4: 파이썬 생존 안내서 (자막)

이흥섭게임서버 아키텍트

@ 왓 스튜디오

현재 《야생의 땅: 듀랑고》 프로젝트에서게임서버 아키텍트로 일하고 있습니다.

Page 5: 파이썬 생존 안내서 (자막)

• 《야생의 땅: 듀랑고》

• 《카트라이더 대시》

• 《카트라이더 코인러시》

• 한글라이즈

• Profiling

• TrueSkill 파이썬 구현

이전엔 《카트라이더 대시》와《코인러시》 시리즈의 서버를 만들었고

Page 6: 파이썬 생존 안내서 (자막)

• 《야생의 땅: 듀랑고》

• 《카트라이더 대시》

• 《카트라이더 코인러시》

• 한글라이즈

• Profiling

• TrueSkill 파이썬 구현

틈틈이 GitHub 같은 곳에서오픈소스 활동도 하고 있어요.

Page 7: 파이썬 생존 안내서 (자막)

• NDC 2014 ― 《야생의 땅: 듀랑고》 서버 아키텍처

• 파이콘 KR 2015 ― Profiling

• NDC 2016 ― 《야생의 땅: 듀랑고》 서버 아키텍처 Vol. 2

오늘이 제 4번째 발표인데요

Page 8: 파이썬 생존 안내서 (자막)

• NDC 2014 ― 《야생의 땅: 듀랑고》 서버 아키텍처

• 파이콘 KR 2015 ― Profiling

• NDC 2016 ― 《야생의 땅: 듀랑고》 서버 아키텍처 Vol. 2

재작년과 올해 NDC에서《듀랑고》 서버 아키텍처에 대한 얘기를 했었고

Page 9: 파이썬 생존 안내서 (자막)

• NDC 2014 ― 《야생의 땅: 듀랑고》 서버 아키텍처

• 파이콘 KR 2015 ― Profiling

• NDC 2016 ― 《야생의 땅: 듀랑고》 서버 아키텍처 Vol. 2

작년 파이콘 KR에선 저희가 만든파이썬 프로파일러에 대해 얘기한 적이 있습니다.

Page 10: 파이썬 생존 안내서 (자막)

2006년 파이썬 2.4~

저는 2006년, 최신 파이썬 버전이 2.4이던시절부터 파이썬을 써왔어요.

Page 11: 파이썬 생존 안내서 (자막)

코드

의도

특히 프로그래머의 의도에 비해서

Page 12: 파이썬 생존 안내서 (자막)

코드

의도

써야 되는 코드의 양이 적다는 점이매력으로 느껴졌었는데

Page 13: 파이썬 생존 안내서 (자막)

코드

의도

아무래도 문법도 C 스타일 언어에 비해선짧은 코드에 좀 더 맞춰져 있고

Page 14: 파이썬 생존 안내서 (자막)

코드

의도

표준 라이브러리나성숙한 써드파티 라이브러리도 풍부해서

Page 15: 파이썬 생존 안내서 (자막)

코드

의도

어지간한 기능은 직접 짜는 대신끌어다 쓸 수 있기 때문인 것 같아요.

Page 16: 파이썬 생존 안내서 (자막)

재작년 NDC 발표에서 소개했듯이

Page 17: 파이썬 생존 안내서 (자막)

《야생의 땅: 듀랑고》 서버는파이썬으로 만들고 있습니다.

Page 18: 파이썬 생존 안내서 (자막)

파이썬

Page 19: 파이썬 생존 안내서 (자막)

파이썬

그럼 본격적인 이야기에 앞서간단히 파이썬을 소개해 볼게요.

Page 20: 파이썬 생존 안내서 (자막)

1989년 12월

Guido van Rossum

제가 1989년 12월 생인데마침 파이썬도 같은 달에 시작됐더라고요.

Page 21: 파이썬 생존 안내서 (자막)

1989년 12월

Guido van Rossum

그때부터 Guido van Rossum이만든 언어입니다.

Page 22: 파이썬 생존 안내서 (자막)

• https://github.com/python

• PEP https://www.python.org/dev/peps/

• 파이콘

파이썬은 오픈소스고

Page 23: 파이썬 생존 안내서 (자막)

• https://github.com/python

• PEP https://www.python.org/dev/peps/

• 파이콘

사람들이 PEP이라는파이썬 개선 제안서를 제출하면

Page 24: 파이썬 생존 안내서 (자막)

• https://github.com/python

• PEP https://www.python.org/dev/peps/

• 파이콘

토론한 후 채택하거나반려하는 식으로 발전해왔어요.

Page 25: 파이썬 생존 안내서 (자막)

• https://github.com/python

• PEP https://www.python.org/dev/peps/

• 파이콘

파이콘은 파이썬 공식 컨퍼런스인데

Page 26: 파이썬 생존 안내서 (자막)

• https://github.com/python

• PEP https://www.python.org/dev/peps/

• 파이콘

전세계에서 정기적으로 열리고 있고2014년부터는 한국에서도 진행되고 있습니다.

Page 27: 파이썬 생존 안내서 (자막)

import this

class Example(object):

pass

def example():

return Example()

print 'Hello, world'

파이썬 코드는 대충 이렇게 생겼어요.

Page 28: 파이썬 생존 안내서 (자막)

import this

class Example(object):

pass

def example():

return Example()

print 'Hello, world'

모듈 체계가 있고

Page 29: 파이썬 생존 안내서 (자막)

import this

class Example(object):

pass

def example():

return Example()

print 'Hello, world'

클래스와 함수가 있고

Page 30: 파이썬 생존 안내서 (자막)

import this

class Example(object):

pass

def example():

return Example()

print 'Hello, world'

Java나 C# 등과 다르게 꼭 메소드 속이아니더라도 코드를 실행할 수 있어요.

Page 31: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x < 4:

break

else:

print 'not found'

그리고 은근히 편한 문법이 많은데

Page 32: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x < 4:

break

else:

print 'not found'

가령 숫자 범위를 검사할 때

Page 33: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x and x < 4:

break

else:

print 'not found'

부등식 2개를 and로 잇는 대신

Page 34: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x < 4:

break

else:

print 'not found'

부등호를 숫자 양 옆에바로 쓸 수도 있고

Page 35: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x < 4:

break

else:

print 'not found'

if 문 뿐만 아니라루프에도 else 절을 달 수 있어서

Page 36: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x < 4:

break

else:

print 'not found'

루프가 중간에 멈췄는지아니면 끝까지 무사히 돌았는지

Page 37: 파이썬 생존 안내서 (자막)

for x in numbers:

if 2 < x < 4:

break

else:

print 'not found'

표시해둘 변수를따로 두지 않아도 되죠.

Page 38: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

다들 아시다시피파이썬은 동적 타입 언어예요.

Page 39: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

컴파일되는 언어도 아니고타입을 결정한 채 런타임을 돌릴 수도 없죠.

Page 40: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

그렇다고 타입개념이아주 희박하진 않은데

Page 41: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

이 사분면은 〈 〉란글에서 따왔어요.

Dynamic Typing is NOT Weak Typing

Page 42: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

여기서 “강타입”과 “약타입”은

Page 43: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

얼마나 코드에명시적으로 쓰지 않고도

Page 44: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

저절로 타입캐스팅 되는 경우가많은지를 가리켜요.

Page 45: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

이 사분면에서 파이썬은이쯤 위치한다고 하네요.

Page 46: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

아주 강타입인 것도 아니지만그렇다고 JavaScript나 PHP처럼

Page 47: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

나도 모르게 숫자와 문자열 사이를왔다 갔다 하지도 않습니다.

Page 48: 파이썬 생존 안내서 (자막)

Erlang

Clojure

Python

Groovy

Ruby

Magik

C#

F#

Scala

Haskell

C

C++

Perl

VB

PHP

JavaScript

Java

동적 정적

대신 런타임 에러가 나죠.

Page 49: 파이썬 생존 안내서 (자막)

과학SciPy, NumPy, matplotlib

파이썬은 과학계에서오랫동안 깊게 쓰여왔는데

Page 50: 파이썬 생존 안내서 (자막)

과학SciPy, NumPy, matplotlib

scipy, numpy, matplotlib 같은라이브러리가 이 방면에서 아주 유명합니다.

Page 51: 파이썬 생존 안내서 (자막)

데이터분석Jupyter, Pandas, PySpark

요즘은 Scala와 더불어서데이터분석에도 많이 쓰인다고 해요.

Page 52: 파이썬 생존 안내서 (자막)

데이터분석Jupyter, Pandas, PySpark

Jupyter와 Pandas를 이용해서데이터에 쿼리를 날려보고

Page 53: 파이썬 생존 안내서 (자막)

데이터분석Jupyter, Pandas, PySpark

결과를 읽어서 새로운 통찰을 얻은 다음다시 새로운 쿼리를 날려보는

Page 54: 파이썬 생존 안내서 (자막)

데이터분석Jupyter, Pandas, PySpark

“탐색적 자료 분석”을많이 추천하더라고요.

Page 55: 파이썬 생존 안내서 (자막)

기계학습Theano, TensorFlow

김영호, 2016

과학계의 연장인지 최근엔기계학습 쪽에서도 많이 쓰이고 있어요.

Page 56: 파이썬 생존 안내서 (자막)

기계학습Theano, TensorFlow

김영호, 2016

이 영상은 저희 스튜디오 김영호 님이

Page 57: 파이썬 생존 안내서 (자막)

기계학습Theano, TensorFlow

김영호, 2016

Google에서 만든 딥러닝 라이브러리인TensorFlow를 이용해서

Page 58: 파이썬 생존 안내서 (자막)

기계학습Theano, TensorFlow

김영호, 2016

알파고처럼 AI에게총알 피하기 게임을 학습시킨 모습이에요.

Page 59: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

Ruby에 Rails가 있듯이

Page 60: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

파이썬에도 Flask나 Django 같은훌륭한 웹 프레임워크들이 있는데

Page 61: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

우리나라 스타트업에서많이 쓰이고 있는 것 같아요.

Page 62: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

제 홈페이지도 Flask로 만들어져 있죠.

Page 63: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

또 Requests라고 정말 쓰기 편한HTTP 클라이언트 라이브러리가 있는데

Page 64: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

이게 너무 편해서 표준 라이브러리를 꺾고사실상 표준이 됐어요.

Page 65: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

저 같은 경우 여러가지RESTful API 클라이언트 만들 때

Page 66: 파이썬 생존 안내서 (자막)

웹Flask, Django, Requests

@app.route('/')def hello():

return 'Hello, world'

Requests 덕을 많이 봤습니다.

Page 67: 파이썬 생존 안내서 (자막)

게임이브온라인, 페리아연대기, pygame

게임 쪽 사례로는《이브온라인》이 가장 유명한데

Page 68: 파이썬 생존 안내서 (자막)

게임이브온라인, 페리아연대기, pygame

코루틴을 지원하는 변종 파이썬인Stackless Python으로 서버를 만들었어요.

Page 69: 파이썬 생존 안내서 (자막)

게임이브온라인, 페리아연대기, pygame

《페리아연대기》에서는 유저 스크립트 언어로파이썬을 쓰는 것 같았습니다.

Page 70: 파이썬 생존 안내서 (자막)

게임이브온라인, 페리아연대기, pygame

pygame이라는아주 작은 게임 엔진도 있는데

Page 71: 파이썬 생존 안내서 (자막)

게임이브온라인, 페리아연대기, pygame

복잡한 건 만들기 어렵지만간단한 게임 클라이언트 만들기에는 좋아요.

Page 72: 파이썬 생존 안내서 (자막)

게임이브온라인, 페리아연대기, pygame

아까 보신 김영호 님의 총알피하기 게임도클라이언트를 이것으로 만들었어요.

Page 73: 파이썬 생존 안내서 (자막)

데스크톱 자동화SikuliX, AutoPy

click( )

type('cmd')

type(Key.ENTER)

그밖에 데스크탑 매크로를

Page 74: 파이썬 생존 안내서 (자막)

데스크톱 자동화SikuliX, AutoPy

click( )

type('cmd')

type(Key.ENTER)

스크립트로 짤 수 있게 해주는프레임워크도 몇 가지 있으니

Page 75: 파이썬 생존 안내서 (자막)

데스크톱 자동화SikuliX, AutoPy

click( )

type('cmd')

type(Key.ENTER)

프로그래밍과는 거리가 먼 사람도,특히 사무직이라면

Page 76: 파이썬 생존 안내서 (자막)

데스크톱 자동화SikuliX, AutoPy

click( )

type('cmd')

type(Key.ENTER)

이런 것으로 업무 생산성을많이 높일 수 있을 것 같아요.

Page 77: 파이썬 생존 안내서 (자막)

Awesome Pythonhttp://awesome-python.com/

이곳에 들어가시면제가 소개한 분야 외에도

Page 78: 파이썬 생존 안내서 (자막)

Awesome Pythonhttp://awesome-python.com/

파이썬이 어떻게 쓰이고 있는지살펴볼 수 있습니다.

Page 79: 파이썬 생존 안내서 (자막)

•듀랑고 서버

•유닛테스트

•빌드/배포

•각종 CLI 툴

이제 본론으로 들어가 볼게요.

Page 80: 파이썬 생존 안내서 (자막)

•듀랑고 서버

•유닛테스트

•빌드/배포

•각종 CLI 툴

저희 스튜디오에선 파이썬을《듀랑고》 게임서버와 유닛테스트,

Page 81: 파이썬 생존 안내서 (자막)

•듀랑고 서버

•유닛테스트

•빌드/배포

•각종 CLI 툴

빌드와 배포, 그리고 각종 커맨드라인 툴을만드는 데 쓰고 있습니다.

Page 82: 파이썬 생존 안내서 (자막)

1. 파이썬, 인터프리터

2. 동시성

3. 웹 서버

4. 터미널 앱

5. 빌드와 배포

6. 컨벤션 통일

7. REPL

8. 디버깅

9. 자료구조

10. 제너레이터

11. 성능 측정

12. 로드타임

13. 메모리 최적화

14. 흔한 실수

15. 오픈소스

저희가 이런 걸 만들 때어떤 재료를 써왔고

Page 83: 파이썬 생존 안내서 (자막)

1. 파이썬, 인터프리터

2. 동시성

3. 웹 서버

4. 터미널 앱

5. 빌드와 배포

6. 컨벤션 통일

7. REPL

8. 디버깅

9. 자료구조

10. 제너레이터

11. 성능 측정

12. 로드타임

13. 메모리 최적화

14. 흔한 실수

15. 오픈소스

또 어떤 재료를 안 썼거나혹은 못 썼는지

Page 84: 파이썬 생존 안내서 (자막)

1. 파이썬, 인터프리터

2. 동시성

3. 웹 서버

4. 터미널 앱

5. 빌드와 배포

6. 컨벤션 통일

7. REPL

8. 디버깅

9. 자료구조

10. 제너레이터

11. 성능 측정

12. 로드타임

13. 메모리 최적화

14. 흔한 실수

15. 오픈소스

그리고 코드 품질은어떻게 관리하고 개선하고 있는지

Page 85: 파이썬 생존 안내서 (자막)

1. 파이썬, 인터프리터

2. 동시성

3. 웹 서버

4. 터미널 앱

5. 빌드와 배포

6. 컨벤션 통일

7. REPL

8. 디버깅

9. 자료구조

10. 제너레이터

11. 성능 측정

12. 로드타임

13. 메모리 최적화

14. 흔한 실수

15. 오픈소스

주제가 꽤 다양하긴 한데하나씩 살펴보겠습니다.

Page 86: 파이썬 생존 안내서 (자막)

파이썬,인터프리터

Page 87: 파이썬 생존 안내서 (자막)

파이썬,인터프리터

저희가 쓰는 파이썬 버전과인터프리터에 대해 먼저 얘기해 볼게요.

Page 88: 파이썬 생존 안내서 (자막)

CPython, PyPy, Jython,

IronPython, Python for .NET

2.6, 2.7, 3.4, 3.5

파이썬엔 여러가지인터프리터 구현이 있고

Page 89: 파이썬 생존 안내서 (자막)

CPython, PyPy, Jython,

IronPython, Python for .NET

2.6, 2.7, 3.4, 3.5

언어 버전에도여러가지가 있어요.

Page 90: 파이썬 생존 안내서 (자막)

CPython, PyPy, Jython,

IronPython, Python for .NET

2.6, 2.7, 3.4, 3.5

그 중 저희가 사용하는 인터프리터는C로 만든 공식 구현체인 CPython,

Page 91: 파이썬 생존 안내서 (자막)

CPython, PyPy, Jython,

IronPython, Python for .NET

2.6, 2.7, 3.4, 3.5

그리고 언어 버전은 2.7이에요.

Page 92: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

파이썬 2는 3와완전히 호환되진 않는데

Page 93: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

2.7은 그런 파이썬 2 중에서마지막 버전이죠.

Page 94: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

파이썬 언어 개발자들은 지금3에 주력하고 있어서

Page 95: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

3에서 이룬 수 많은 개선점을2에는 제공해주지 않고

Page 96: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

버그픽스와 보안패치정도만 해주고 있습니다.

Page 97: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

그래서 지금은 굳이2를 쓸 이유가 없지만

Page 98: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

저희가 서버를 만들기 시작한2013년에만 해도

Page 99: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

3를 지원하는 써드파티 라이브러리가그렇게 많지 않아서 2.7을 선택했었는데

Page 100: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

몇 년 새 상황이 많이달라졌더라고요.

Page 101: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

요즘은 써드파티 라이브러리도어지간해선 3를 지원해요.

Page 102: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

저희도 넘어가고 싶지만

Page 103: 파이썬 생존 안내서 (자막)

파이썬 2.7

•파이썬 2 마지막 마이너 버전

•신기능 패치 중단, 버그/보안 패치는 유지

•2013년엔 라이브러리 호환성이 가장 좋았음.

이미 2에 맞춰서 짠 코드가 많아서쉽게 넘어가진 못 하고 있습니다.

Page 104: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

파이썬 3를 2와 비교해보면우선 문자열과 바이트열 구분이 명확해져서

Page 105: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

둘이 섞어 써서 인코딩 오류가 나는 참사는발생하지 않아요.

Page 106: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

또 기존에 있던 표준 라이브러리는깔끔하게 정돈됐고

Page 107: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

다만 이 과정에서하위호환성이 많이 깨졌죠.

Page 108: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

또 새로운 표준 라이브러리도여러가지 생겼는데

Page 109: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

특히 비동기 I/O 라이브러리인 asyncio나

Page 110: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

파이썬에서 정적 타입힌트를달 수 있게 해주는 typing 같은 게

Page 111: 파이썬 생존 안내서 (자막)

파이썬 3?

•명확한 문자열/바이트열 구분

•정돈되고 강해진 표준 라이브러리

•특히 asyncio, typing

아주 탐나더라고요.못 써서 아쉽습니다.

Page 112: 파이썬 생존 안내서 (자막)

from __future__ import unicode_literals

import six

아쉬운 대로 저희는 다음 파이썬 버전의특성 중 일부를 차용할 수 있게 해주는

Page 113: 파이썬 생존 안내서 (자막)

from __future__ import unicode_literals

import six

__future__ 모듈로 3의 특성 중유용한 몇 가지를 가져와서 쓰고 있어요.

Page 114: 파이썬 생존 안내서 (자막)

from __future__ import unicode_literals

import six

한편 저희 내부 코드가 아닌외부에 공개할 오픈소스 라이브러리를 만들 땐

Page 115: 파이썬 생존 안내서 (자막)

from __future__ import unicode_literals

import six

같은 코드로 파이썬 2와 3를함께 지원하기 위해서

Page 116: 파이썬 생존 안내서 (자막)

from __future__ import unicode_literals

import six

six라는 라이브러리로호환성을 갖추고 있어요.

Page 117: 파이썬 생존 안내서 (자막)

from __future__ import unicode_literals

import six=6=2×3

여기서 six는2 곱하기 3을 뜻한다네요.

Page 118: 파이썬 생존 안내서 (자막)

PyPy?파이썬으로 만든 파이썬 인터프리터

공식 구현체인 CPython 말고

Page 119: 파이썬 생존 안내서 (자막)

PyPy?파이썬으로 만든 파이썬 인터프리터

파이썬으로 만든 파이썬 인터프리터인PyPy도 추천합니다.

Page 120: 파이썬 생존 안내서 (자막)

PyPy?파이썬으로 만든 파이썬 인터프리터

PyPy는 JavaScript의 V8 처럼파이썬 코드를 JIT 컴파일 해주는데요

Page 121: 파이썬 생존 안내서 (자막)

PyPy > CPython

7.5x

여기서 내세우는 비교자료를 보면CPython보다 7.5배 정도 빠르다고 합니다.

Page 122: 파이썬 생존 안내서 (자막)

PyPy > CPython

7.5x

다만 C로 구현된 라이브러리 같은 경우는호환되지 않을 때도 종종 있어서

Page 123: 파이썬 생존 안내서 (자막)

PyPy > CPython

7.5x

저희는 아직 채택하지 못 했어요.

Page 124: 파이썬 생존 안내서 (자막)

파이썬 3나 PyPy

저희는 파이썬 2.7과 CPython둘 다 벗어나지 못 했지만

Page 125: 파이썬 생존 안내서 (자막)

파이썬 3나 PyPy

그래도 언젠가는파이썬 3나 PyPy로 넘어가고자 합니다.

Page 126: 파이썬 생존 안내서 (자막)

파이썬 3나 PyPy

여러분이 만약 새 프로젝트를 시작하신다면저희보다 나은 선택을 하시길 바랄게요.

Page 127: 파이썬 생존 안내서 (자막)

동시성

Page 128: 파이썬 생존 안내서 (자막)

동시성

다음 주제는 동시성이에요.

Page 129: 파이썬 생존 안내서 (자막)

gevent코루틴 기반 비동기 I/O

저흰 gevent라는 코루틴 기반비동기 I/O 라이브러리를 쓰고 있습니다.

Page 130: 파이썬 생존 안내서 (자막)

greenlet코루틴

http://lee-seungjae.github.io/greenlet.html ― 이승재, 2011

원래 파이썬엔 코루틴이 없지만greenlet이라는 라이브러리가

Page 131: 파이썬 생존 안내서 (자막)

greenlet코루틴

파이썬을 마개조해서코루틴을 쓸 수 있게 해줬어요.

http://lee-seungjae.github.io/greenlet.html ― 이승재, 2011

Page 132: 파이썬 생존 안내서 (자막)

greenlet코루틴

이 라이브러리는PyPy 만든 Armin Rigo와

http://lee-seungjae.github.io/greenlet.html ― 이승재, 2011

Page 133: 파이썬 생존 안내서 (자막)

greenlet코루틴

Stackless Python 만든Christian Tismer의 합작이에요.

http://lee-seungjae.github.io/greenlet.html ― 이승재, 2011

Page 134: 파이썬 생존 안내서 (자막)

greenlet코루틴

아래 링크는 예전에데브캣스튜디오 이승재 님께서

http://lee-seungjae.github.io/greenlet.html ― 이승재, 2011

Page 135: 파이썬 생존 안내서 (자막)

greenlet코루틴

greenlet에 대해 쓰셨던 글인데기억에 남아서 한 번 가져와봤습니다.

http://lee-seungjae.github.io/greenlet.html ― 이승재, 2011

Page 136: 파이썬 생존 안내서 (자막)

g1 = gevent.spawn(requests.get, url1)

g2 = gevent.spawn(requests.get, url2)

g3 = gevent.spawn(requests.get, url3)

gevent.joinall([g1, g2, g3])

print([g.value for g in [g1, g2, g3]])

코루틴을 직접 다루는 건굉장히 까다로워요.

Page 137: 파이썬 생존 안내서 (자막)

g1 = gevent.spawn(requests.get, url1)

g2 = gevent.spawn(requests.get, url2)

g3 = gevent.spawn(requests.get, url3)

gevent.joinall([g1, g2, g3])

print([g.value for g in [g1, g2, g3]])

gevent는 다루기 까다로운greenlet 코루틴을

Page 138: 파이썬 생존 안내서 (자막)

g1 = gevent.spawn(requests.get, url1)

g2 = gevent.spawn(requests.get, url2)

g3 = gevent.spawn(requests.get, url3)

gevent.joinall([g1, g2, g3])

print([g.value for g in [g1, g2, g3]])

평범한 멀티스레딩 코드처럼쓸 수 있게 해주죠.

Page 139: 파이썬 생존 안내서 (자막)

g1 = gevent.spawn(requests.get, url1)

g2 = gevent.spawn(requests.get, url2)

g3 = gevent.spawn(requests.get, url3)

gevent.joinall([g1, g2, g3])

print([g.value for g in [g1, g2, g3]])

특정한 일을 하는스레드를 띄우고

Page 140: 파이썬 생존 안내서 (자막)

g1 = gevent.spawn(requests.get, url1)

g2 = gevent.spawn(requests.get, url2)

g3 = gevent.spawn(requests.get, url3)

gevent.joinall([g1, g2, g3])

print([g.value for g in [g1, g2, g3]])

다른 스레드가 일을 다 할 때까지 기다려서동기화 하는 식이에요.

Page 141: 파이썬 생존 안내서 (자막)

g1 = gevent.spawn(requests.get, url1)

g2 = gevent.spawn(requests.get, url2)

g3 = gevent.spawn(requests.get, url3)

gevent.joinall([g1, g2, g3])

print([g.value for g in [g1, g2, g3]])

평범하죠?

Page 142: 파이썬 생존 안내서 (자막)

Greenlet

Thread

gevent 스레드는방금 얘기한 라이브러리 이름과 같은

Page 143: 파이썬 생존 안내서 (자막)

Greenlet

Thread

“Greenlet”이라고 불러요.

Page 144: 파이썬 생존 안내서 (자막)

Greenlet

Thread

Greenlet은 유저스페이스에서 도는경량 스레드라서

Page 145: 파이썬 생존 안내서 (자막)

Greenlet

Thread

표준 스레드보다오버헤드가 훨씬 적고 가벼워요.

Page 146: 파이썬 생존 안내서 (자막)

Greenlet

Thread

그래서 수천 개씩띄워서 쓸 수 있는데

Page 147: 파이썬 생존 안내서 (자막)

Greenlet

Thread

특히 네트워크를 많이 다룰 때높은 동시성을 얻을 수 있죠.

Page 148: 파이썬 생존 안내서 (자막)

if key in GLOBAL_DICT:

gevent.sleep(0.001)

del GLOBAL_DICT[key]

쓰는 법이 멀티스레딩과 다르지 않다 보니코딩할 때 고달플 때도 있어요.

Page 149: 파이썬 생존 안내서 (자막)

if key in GLOBAL_DICT:

gevent.sleep(0.001)

del GLOBAL_DICT[key]

이렇게 첫 줄에서아무리 조건을 확보해 놔도

Page 150: 파이썬 생존 안내서 (자막)

if key in GLOBAL_DICT:

gevent.sleep(0.001)

del GLOBAL_DICT[key]

스레드 봉쇄가 발생한 이후에는

Page 151: 파이썬 생존 안내서 (자막)

if key in GLOBAL_DICT:

gevent.sleep(0.001)

del GLOBAL_DICT[key] KeyError!

조건이 무효해질 수 있거든요.

Page 152: 파이썬 생존 안내서 (자막)

if key in GLOBAL_DICT:

gevent.sleep(0.001)

del GLOBAL_DICT[key] KeyError!

그럼에도 저희가gevent를 택했던 이유는

Page 153: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

이미 동기식으로 작성돼있는

Page 154: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

표준 라이브러리를 비롯한수 많은 라이브러리를

Page 155: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

모두 그대로 쓸 수 있다는 점 때문이었어요.

Page 156: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

gevent가 제공하는 멍키패칭을 돌리면

Page 157: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

threading이나 socket 같은표준 라이브러리가

Page 158: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

전부 gevent 용으로 갈아치워져서

Page 159: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

비동기 I/O를 고려하지 않던 기존 코드까지도모두 동시에 실행할 수 있게 되거든요.

Page 160: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

JavaScript처럼 콜백을 등록한다거나C#처럼 async, await 키워드를 쓴다거나

Page 161: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

그런 특별한 코딩법을 필요로 하는다른 동시성 모델로는

Page 162: 파이썬 생존 안내서 (자막)

import gevent.monkey

gevent.monkey.patch_all()

달성할 수 없는 강점이죠.

Page 163: 파이썬 생존 안내서 (자막)

동시성 (Concurrency)

병렬성 (Parallelism)

단 gevent로 확보할 수 있는 건동시성이지 병렬성은 아닌데

Page 164: 파이썬 생존 안내서 (자막)

동시성 (Concurrency)

병렬성 (Parallelism)

수 많은 I/O를 동시에 수행할 순 있지만실제로는 싱글스레디드로 돌아서

Page 165: 파이썬 생존 안내서 (자막)

동시성 (Concurrency)

병렬성 (Parallelism)

여러 CPU 코어를 활용할 순 없어요.

Page 166: 파이썬 생존 안내서 (자막)

GIL

사실 이 점은 파이썬표준 멀티스레딩도 마찬가지입니다.

Page 167: 파이썬 생존 안내서 (자막)

GIL

파이썬엔 역사적인 이유로Global Interpreter Lock이란 게 있는데

Page 168: 파이썬 생존 안내서 (자막)

GIL

이 때문에 파이썬 바이트코드는동시에 여러 스레드에서 실행될 수 없거든요.

Page 169: 파이썬 생존 안내서 (자막)

+멀티프로세싱

그래서 저희는 부족한 병렬성을 보완하기 위해멀티프로세싱도 같이 쓰고 있습니다.

Page 170: 파이썬 생존 안내서 (자막)

def hang():

while True:

pass

gevent.spawn(hang)

표준 멀티스레딩과 달리gevent를 쓸 땐 주의해야 할 점이 있는데요

Page 171: 파이썬 생존 안내서 (자막)

def hang():

while True:

pass

gevent.spawn(hang)

선점형 멀티태스킹이 아니다 보니한 스레드가 쉴 새 없이 돌면

Page 172: 파이썬 생존 안내서 (자막)

def hang():

while True:

pass

gevent.spawn(hang)

다른 스레드로는기회가 넘어가지 않는다는 거예요.

Page 173: 파이썬 생존 안내서 (자막)

def hang():

while True:

pass

gevent.spawn(hang)

특히 게임서버에서는이런 일이 절대 생기면 안 되겠죠.

Page 174: 파이썬 생존 안내서 (자막)

def hang():

while True:

gevent.idle()

gevent.spawn(hang)

꼭 sleep이나 idle이라도 넣어서

Page 175: 파이썬 생존 안내서 (자막)

def hang():

while True:

gevent.idle()

gevent.spawn(hang)

스레드가 잠시나마 봉쇄되게끔 만들어야이런 일을 방지할 수 있어요.

Page 176: 파이썬 생존 안내서 (자막)

asyncio?파이썬 3.4~

한편 파이썬 3.4부터는asyncio라는

Page 177: 파이썬 생존 안내서 (자막)

asyncio?파이썬 3.4~

차세대 비동기 I/O 라이브러리를쓸 수 있는데

Page 178: 파이썬 생존 안내서 (자막)

@asyncio.coroutine

def main():

f1 = aiohttp.get(url1)

f2 = aiohttp.get(url2)

f3 = aiohttp.get(url3)

for fut in [f1, f2, f3]:

yield from fut

print([fut.result() for fut in [f1, f2, f3]])

파이썬의 언어 기능인 제너레이터를 이용해서명시적인 코루틴을 구현하고 있어요.

Page 179: 파이썬 생존 안내서 (자막)

@asyncio.coroutine

def main():

f1 = aiohttp.get(url1)

f2 = aiohttp.get(url2)

f3 = aiohttp.get(url3)

for fut in [f1, f2, f3]:

yield from fut

print([fut.result() for fut in [f1, f2, f3]])

gevent와 다르게 코드만 봐도어디서 봉쇄가 발생할지 명확하게 보이죠.

Page 180: 파이썬 생존 안내서 (자막)

async def main():

f1 = aiohttp.get(url1)

f2 = aiohttp.get(url2)

f3 = aiohttp.get(url3)

for fut in [f1, f2, f3]:

await fut

print([fut.result() for fut in [f1, f2, f3]])

파이썬 3.5에서는 아예 C#처럼async와 await 키워드까지 추가돼서

Page 181: 파이썬 생존 안내서 (자막)

async def main():

f1 = aiohttp.get(url1)

f2 = aiohttp.get(url2)

f3 = aiohttp.get(url3)

for fut in [f1, f2, f3]:

await fut

print([fut.result() for fut in [f1, f2, f3]])

더 편하게 쓸 수 있게 됐어요.

Page 182: 파이썬 생존 안내서 (자막)

async def main():

f1 = aiohttp.get(url1)

f2 = aiohttp.get(url2)

f3 = aiohttp.get(url3)

for fut in [f1, f2, f3]:

await fut

print([fut.result() for fut in [f1, f2, f3]])

다만 asyncio를 쓰면

Page 183: 파이썬 생존 안내서 (자막)

async def main():

f1 = aiohttp.get(url1)

f2 = aiohttp.get(url2)

f3 = aiohttp.get(url3)

for fut in [f1, f2, f3]:

await fut

print([fut.result() for fut in [f1, f2, f3]])

asyncio에 맞춰진 라이브러리만써야해서 선택의 폭이 좁아지긴 해요.

Page 184: 파이썬 생존 안내서 (자막)

aiobotocore, aiohttp, aiokafka,

aiomcache, aiomysql, aioredis,

aiorwlock, aiozmq

그래도 보통 이름에 “aio”라는 접두사가붙어있으니까 쉽게 구별할 순 있을 겁니다.

Page 185: 파이썬 생존 안내서 (자막)

•멀티프로세싱

•멀티스레딩

•asyncio

•gevent

•eventlet

•Twisted

제가 소개한 것 외에도 파이썬에선여러가지 방법으로 동시성을 확보할 수 있어요.

Page 186: 파이썬 생존 안내서 (자막)

•멀티프로세싱

•멀티스레딩

•asyncio

•gevent

•eventlet

•Twisted

각 방법에는 서로 다른트레이드오프가 있죠.

Page 187: 파이썬 생존 안내서 (자막)

•멀티프로세싱

•멀티스레딩

•asyncio

•gevent

•eventlet

•Twisted

만약 여러분도파이썬으로 서버를 만드신다면

Page 188: 파이썬 생존 안내서 (자막)

•멀티프로세싱

•멀티스레딩

•asyncio

•gevent

•eventlet

•Twisted

이런 방법들을 한 번 비교해보고가장 적합한 걸 고르시면 좋을 것 같습니다.

Page 189: 파이썬 생존 안내서 (자막)

웹 서버

Page 190: 파이썬 생존 안내서 (자막)

웹 서버

저희 게임서버는 대부분이소켓을 직접 다루는 데디케이티드 서버지만

Page 191: 파이썬 생존 안내서 (자막)

웹 서버

일부는 RESTful한 웹 서버로 돼있어요.

Page 192: 파이썬 생존 안내서 (자막)

Flask

거기엔 앞에서도 소개했던 Flask라는간단한 웹 서버 프레임워크를 쓰는데

Page 193: 파이썬 생존 안내서 (자막)

•Werkzeug 웹 서버 유틸리티

•Jinja2 템플릿 엔진

Flask는 Werkzeug라는웹 서버 유틸리티와

Page 194: 파이썬 생존 안내서 (자막)

•Werkzeug 웹 서버 유틸리티

•Jinja2 템플릿 엔진

Jinja2라고, 보통 HTML 페이지 찍어낼 때쓰는 템플릿엔진 위에서

Page 195: 파이썬 생존 안내서 (자막)

•템플릿 엔진

•URL 라우팅

•세션

•서브도메인

•JSON 지원

•디버깅 콘솔

몇 가지 편의 기능을 함께 제공하는작은 웹 서버 프레임워크예요.

Page 196: 파이썬 생존 안내서 (자막)

•템플릿 엔진

•URL 라우팅

•세션

•서브도메인

•JSON 지원

•디버깅 콘솔

작은 프레임워크이긴 하지만

Page 197: 파이썬 생존 안내서 (자막)

•템플릿 엔진

•URL 라우팅

•세션

•서브도메인

•JSON 지원

•디버깅 콘솔

일반적으로 웹 개발할 때 필요한간단한 기능들은 대부분 갖추고 있고

Page 198: 파이썬 생존 안내서 (자막)

•템플릿 엔진

•URL 라우팅

•세션

•서브도메인

•JSON 지원

•디버깅 콘솔

좀 더 복잡한 기능도플러그인으로 쉽게 구할 수 있어요.

Page 199: 파이썬 생존 안내서 (자막)

app = Flask(__name__)

@app.route('/')

def index():

return 'Hello, world', 200

간단한 Flask 예제예요.

Page 200: 파이썬 생존 안내서 (자막)

app = Flask(__name__)

@app.route('/')

def index():

return 'Hello, world', 200

데코레이터를 이용해서 URL 라우팅 테이블을직관적으로 만들어내는 게 특징이죠.

Page 201: 파이썬 생존 안내서 (자막)

#include "crow.h"

int main() {

crow::SimpleApp app;

CROW_ROUTE(app, "/")([](){

return "Hello world";

});

}

우리 회사 하재승 님이 만드신

Page 202: 파이썬 생존 안내서 (자막)

#include "crow.h"

int main() {

crow::SimpleApp app;

CROW_ROUTE(app, "/")([](){

return "Hello world";

});

}

C++ 웹 프레임워크 Crow가여기서 영감을 얻었다고 합니다.

Page 203: 파이썬 생존 안내서 (자막)

WSGIWeb Server Gateway Interface

파이썬엔 WSGI라는 표준화된웹 애플리케이션 인터페이스가 있어요.

Page 204: 파이썬 생존 안내서 (자막)

WSGIWeb Server Gateway Interface

Flask를 비롯해 아마도 모든파이썬 웹 서버 라이브러리가

Page 205: 파이썬 생존 안내서 (자막)

WSGIWeb Server Gateway Interface

이 인터페이스를 따르고 있을 거예요.

Page 206: 파이썬 생존 안내서 (자막)

app = Flask(__name__)

server = gevent.pywsgi.WSGIServer(socket, app)

server.serve_forever()

이 표준 인터페이스 덕분에Flask를 gevent에도 잘 붙일 수 있었어요.

Page 207: 파이썬 생존 안내서 (자막)

app = Flask(__name__)

server = gevent.pywsgi.WSGIServer(socket, app)

server.serve_forever()

gevent엔 고성능 WSGI 서버가 들어있는데

Page 208: 파이썬 생존 안내서 (자막)

app = Flask(__name__)

server = gevent.pywsgi.WSGIServer(socket, app)

server.serve_forever()

이것으로 Flask 웹 서버를바로 서빙할 수 있는 거죠.

Page 209: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

yield 'Hello, world\n'

WSGI 애플리케이션은 단순히

Page 210: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

yield 'Hello, world\n'

HTTP 요청정보를 담는 environ 인자와

Page 211: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

yield 'Hello, world\n'

HTTP 응답시작을 개시하는 함수인start_response 인자를 받아서

Page 212: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

yield 'Hello, world\n'

응답 헤더를 결정한 후start_response를 불러주고

Page 213: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

yield 'Hello, world\n'

내용을 브러우저로 스트리밍할 수 있게끔yield하거나

Page 214: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

return ['Hello, world\n']

시퀀스 형태로바로 return하는 함수예요.

Page 215: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

return ['Hello, world\n']

이 인터페이스에만 맞추면파이썬의 거의 모든 웹 도구를 같이 쓸 수 있어요.

Page 216: 파이썬 생존 안내서 (자막)

def wsgi_app(environ, start_response):

headers = [('Content-Type', 'text/plain')]

start_response('200 OK', headers)

return ['Hello, world\n']

하지만 생긴 걸 보면 알 수 있듯이

Page 217: 파이썬 생존 안내서 (자막)

HTTP/2.0, WebSocket

아쉽게도 서버사이드 푸시 같은HTTP/2.0 기능이나

Page 218: 파이썬 생존 안내서 (자막)

HTTP/2.0, WebSocket

웹소켓 같은 요즘 프로토콜을구현하진 못해요.

Page 219: 파이썬 생존 안내서 (자막)

HTTP/2.0, WebSocket

그래도 메일링리스트에서논의는 되고 있으니까

Page 220: 파이썬 생존 안내서 (자막)

HTTP/2.0, WebSocket

언젠가 차세대 WSGI가나오길 기대해봅니다.

Page 221: 파이썬 생존 안내서 (자막)

Django?

파이썬 웹 프레임워크 중에선저는 안 써봤지만

Page 222: 파이썬 생존 안내서 (자막)

Django?

Flask보단 Django가훨씬 유명하고 많이 쓰여요.

Page 223: 파이썬 생존 안내서 (자막)

Django

Flask

구글 트렌드에서 찾아보니까인기도가 Flask의 4배 정도는 되더라고요.

Page 224: 파이썬 생존 안내서 (자막)

Instagram과 Pinterest가Django 쓰는 것으로 유명하다고 합니다.

Page 225: 파이썬 생존 안내서 (자막)

• ORM

• 템플릿 엔진

• URL 라우팅

• 폼 검증

• 캐싱

• 국제화

• XML/JSON 지원

Django는 MVC 구조를 내장하고 있어요.

Page 226: 파이썬 생존 안내서 (자막)

• ORM

• 템플릿 엔진

• URL 라우팅

• 폼 검증

• 캐싱

• 국제화

• XML/JSON 지원

그래서 Flask와 달리ORM까지 포함하고 있죠.

Page 227: 파이썬 생존 안내서 (자막)

• ORM

• 템플릿 엔진

• URL 라우팅

• 폼 검증

• 캐싱

• 국제화

• XML/JSON 지원

그 밖에도 통합돼있는 기능이 매우 많은데

Page 228: 파이썬 생존 안내서 (자막)

• ORM

• 템플릿 엔진

• URL 라우팅

• 폼 검증

• 캐싱

• 국제화

• XML/JSON 지원

아마 2000년대 후반에 유행했던Ruby on Rails랑 비슷하지 않을까 싶어요.

Page 229: 파이썬 생존 안내서 (자막)

•WSGI

•Flask

•Django

혹시 파이썬으로 웹 서버를만들려는 분이 계신다면

Page 230: 파이썬 생존 안내서 (자막)

•WSGI

•Flask

•Django

이 두 프레임워크와WSGI 지원하는 도구를 한 번 검토해보세요.

Page 231: 파이썬 생존 안내서 (자막)

터미널 앱

Page 232: 파이썬 생존 안내서 (자막)

터미널 앱

저희는 서버 개발을 보조하기 위해서

Page 233: 파이썬 생존 안내서 (자막)

터미널 앱

터미널에서 쓸 수 있는 도구도많이 만들고 있어요.

Page 234: 파이썬 생존 안내서 (자막)

ClickCLI 프레임워크

Click은 CLI를 쉽게,또 잘 만들 수 있게 해주는 프레임워크예요.

Page 235: 파이썬 생존 안내서 (자막)

ClickCLI 프레임워크

앞에서 소개했던 Flask와 이것은모두 Armin Ronacher가 만들었죠.

Page 236: 파이썬 생존 안내서 (자막)

import click

@click.command()

@click.option('-n', '--name', help='Your name.')

def cli(name):

click.echo('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

cli()

Click 애플리케이션 코드는이렇게 생겼는데

Page 237: 파이썬 생존 안내서 (자막)

import click

@click.command()

@click.option('-n', '--name', help='Your name.')

def cli(name):

click.echo('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

cli()

Flask처럼 데코레이터를적극적으로 활용하고 있어요.

Page 238: 파이썬 생존 안내서 (자막)

import click

@click.command()

@click.option('-n', '--name', help='Your name.')

def cli(name):

click.echo('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

cli()

이렇게 데코레이터로커맨드 옵션을 선언하면

Page 239: 파이썬 생존 안내서 (자막)

import click

@click.command()

@click.option('-n', '--name', help='Your name.')

def cli(name):

click.echo('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

cli()

같은 이름의 매개변수로값이 들어오게 돼요.

Page 240: 파이썬 생존 안내서 (자막)

❯ python cli.py -n "Heungsub Lee"

Hi Heungsub Lee!

❯ python cli.py --help

Usage: cli.py [OPTIONS]

Options:

-n, --name TEXT Your name.

--help Show this message and exit.

돌려보면 이렇게 동작합니다.

Page 241: 파이썬 생존 안내서 (자막)

❯ python cli.py -n "Heungsub Lee"

Hi Heungsub Lee!

❯ python cli.py --help

Usage: cli.py [OPTIONS]

Options:

-n, --name TEXT Your name.

--help Show this message and exit.

보시다시피 리눅스 CLI를상식적으로 잘 구현하고 있다는 점에서

Page 242: 파이썬 생존 안내서 (자막)

❯ python cli.py -n "Heungsub Lee"

Hi Heungsub Lee!

❯ python cli.py --help

Usage: cli.py [OPTIONS]

Options:

-n, --name TEXT Your name.

--help Show this message and exit.

애용하는 프레임워크예요.

Page 243: 파이썬 생존 안내서 (자막)

❯ python cli.py -n "Heungsub Lee"

Hi Heungsub Lee!

❯ python cli.py --help

Usage: cli.py [OPTIONS]

Options:

-n, --name TEXT Your name.

--help Show this message and exit.

상식과 미묘하게 다르게 동작하는프레임워크도 여럿 봐왔거든요.

Page 244: 파이썬 생존 안내서 (자막)

Click을 이용하면이렇게 사용자입력을 받거나

Page 245: 파이썬 생존 안내서 (자막)

프로그레스바를 띄우는 것도 쉽게 할 수 있고색깔도 깔끔하게 다룰 수 있어요.

Page 246: 파이썬 생존 안내서 (자막)

argparse?

Click이 나오기 전에는argparse라는 표준 라이브러리를 썼었는데

Page 247: 파이썬 생존 안내서 (자막)

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-n', '--name', help='Your name.')

def cli(name):

print('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

args = parser.parse_args()

cli(name=args.name)

이렇게

Page 248: 파이썬 생존 안내서 (자막)

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-n', '--name', help='Your name.')

def cli(name):

print('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

args = parser.parse_args()

cli(name=args.name)

옵션을 정의하는 곳과

Page 249: 파이썬 생존 안내서 (자막)

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-n', '--name', help='Your name.')

def cli(name):

print('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

args = parser.parse_args()

cli(name=args.name)

파싱하고 넘겨주는 곳이따로 떨어져 있어서

Page 250: 파이썬 생존 안내서 (자막)

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-n', '--name', help='Your name.')

def cli(name):

print('Hi %s!' % (name or 'NONAME'))

if __name__ == '__main__':

args = parser.parse_args()

cli(name=args.name)

Click에 비해 불편했습니다.

Page 251: 파이썬 생존 안내서 (자막)

UrwidTUI 프레임워크

단순한 CLI 말고리눅스의 top이나 aptitude 같이

Page 252: 파이썬 생존 안내서 (자막)

UrwidTUI 프레임워크

텍스트로 된 UI를 만들고 싶을 땐Urwid라는 TUI 프레임워크를 쓰고 있어요.

Page 253: 파이썬 생존 안내서 (자막)

Urwid를 쓰면 위젯을 조합하는 방식으로꽤 미려한 텍스트 UI를 만들 수 있죠.

Page 254: 파이썬 생존 안내서 (자막)

https://excess.org/article/2012/01/urwid-python-malaysia/

이건 Urwid로 만든 Speedometer라는 건데멋지지 않나요?

Page 255: 파이썬 생존 안내서 (자막)

https://excess.org/article/2012/01/urwid-python-malaysia/

저희 스튜디오에서 만든파이썬 용 프로파일러에도 Urwid를 사용했는데

Page 256: 파이썬 생존 안내서 (자막)

https://excess.org/article/2012/01/urwid-python-malaysia/

그건 뒤에서 성능 측정에 대해얘기할 때 보여드릴게요.

Page 257: 파이썬 생존 안내서 (자막)

curses?

Urwid 말고는 curses라는표준 라이브러리가 대안이 될 수 있어요.

Page 258: 파이썬 생존 안내서 (자막)

curses?

하지만 저는 정말 쓰기 불편했어요.

Page 259: 파이썬 생존 안내서 (자막)

curses?

API가 너무 저수준이고조금만 실수해도 에러가 빵빵 터졌었거든요.

Page 260: 파이썬 생존 안내서 (자막)

win.addstr(y, x, 'Hello, world')

심지어 사용하는 좌표계도

Page 261: 파이썬 생존 안내서 (자막)

win.addstr(y, x, 'Hello, world')

x, y가 아니라 y, x예요.

Page 262: 파이썬 생존 안내서 (자막)

win.addstr(y, x, 'Hello, world')

“curse”가 “저주”란 뜻이잖아요?그 이름값을 하는 거라고 생각합니다.

Page 263: 파이썬 생존 안내서 (자막)

•Click

•Urwid

•argparse

•curses

혹시 bash 같은 쉘 스크립트를 대신하기 위해서파이썬을 검토 중이시라면

Page 264: 파이썬 생존 안내서 (자막)

•Click

•Urwid

•argparse

•curses

Click이랑 Urwid도 같이 검토해보세요.

Page 265: 파이썬 생존 안내서 (자막)

•Click

•Urwid

•argparse

•curses

표준 라이브러리인 argparse나curses보다 분명히 좋습니다.

Page 266: 파이썬 생존 안내서 (자막)

빌드와 배포

Page 267: 파이썬 생존 안내서 (자막)

빌드와 배포

저희는 빌드와 배포를 자동화하는 데에도노력을 많이 기울여왔어요.

Page 268: 파이썬 생존 안내서 (자막)

빌드와 배포

여기에도 파이썬을 많이 쓰고 있죠.

Page 269: 파이썬 생존 안내서 (자막)

build passing

작업자가 Git 저장소에 push하면CI가 자동으로 빌드 스크립트를 돌리면서

Page 270: 파이썬 생존 안내서 (자막)

build passing

컴파일해야 할 건 컴파일하고,코드 품질을 검사한다든가,

Page 271: 파이썬 생존 안내서 (자막)

build passing

유닛테스트를 돌린다든가,뭐 그런 일을 합니다.

Page 272: 파이썬 생존 안내서 (자막)

build passing

빌드에 성공하면 브랜치에 따라서서버에 배포하는 것까지 연계하고 있어요.

Page 273: 파이썬 생존 안내서 (자막)

pytest

이때 유닛테스트 프레임워크로pytest를 쓰는데

Page 274: 파이썬 생존 안내서 (자막)

def test_answer():

assert answer() == 42

pytest를 쓰면딸랑 assert 문만 적어놔도

Page 275: 파이썬 생존 안내서 (자막)

❯ pytest test.py

========= FAILURES =========

E assert 24 == 42

E + where 24 = answer()

def test_answer():

assert answer() == 42

테스트가 실패했을 때 왜 실패했는지단서까지 함께 보고받을 수 있어요.

Page 276: 파이썬 생존 안내서 (자막)

❯ pytest test.py

========= FAILURES =========

E assert 24 == 42

E + where 24 = answer()

def test_answer():

assert answer() == 42

여기 보시면 answer의 결과값이42가 아니라 24였다는 걸 알 수 있죠.

Page 277: 파이썬 생존 안내서 (자막)

def test_answer(self):

self.assertEqual(answer(), 42)

unittest

파이썬엔 unittest라는표준 유닛테스트 프레임워크도 있지만

Page 278: 파이썬 생존 안내서 (자막)

def test_answer(self):

self.assertEqual(answer(), 42)

unittest

이것으로 같은 결과를 얻으려면assertEqual 같은 전용 메소드를 써야만 해요.

Page 279: 파이썬 생존 안내서 (자막)

assertEqual(a, b)

assertNotEqual(a, b)

assertIs(a, b)

assertIn(a, b)

assertGreater(a, b)

unittest

assert a == b

assert a != b

assert a is b

assert a in b

assert a > b

pytest

이런 식으로 비교연산 별로메소드가 따로 마련돼있죠.

Page 280: 파이썬 생존 안내서 (자막)

assertEqual(a, b)

assertNotEqual(a, b)

assertIs(a, b)

assertIn(a, b)

assertGreater(a, b)

unittest

assert a == b

assert a != b

assert a is b

assert a in b

assert a > b

pytest

훨씬 쓰기 번거로워 보이죠?

Page 281: 파이썬 생존 안내서 (자막)

assertEqual(a, b)

assertNotEqual(a, b)

assertIs(a, b)

assertIn(a, b)

assertGreater(a, b)

unittest

assert a == b

assert a != b

assert a is b

assert a in b

assert a > b

pytest

사실 처음에 제가 pytest를쓰기 시작했던 이유는 이 점이 전부였는데

Page 282: 파이썬 생존 안내서 (자막)

assertEqual(a, b)

assertNotEqual(a, b)

assertIs(a, b)

assertIn(a, b)

assertGreater(a, b)

unittest

assert a == b

assert a != b

assert a is b

assert a in b

assert a > b

pytest

쓰다 보니까 테스트 만드는 수고를 덜어주는편의 기능도 많다는 걸 알게 됐어요.

Page 283: 파이썬 생존 안내서 (자막)

@pytest.mark.parametrize('x, y', [

(42, 1), (21, 2), (7, 6)

])

def test_answers(x, y):

assert x * y == 42

가령 테스트케이스에 매개변수를 선언해놓고parametrize 데코레이터를 붙여놓으면

Page 284: 파이썬 생존 안내서 (자막)

@pytest.mark.parametrize('x, y', [

(42, 1), (21, 2), (7, 6)

])

def test_answers(x, y):

assert x * y == 42

알아서 인자를 바꿔가면서 테스트해주고

Page 285: 파이썬 생존 안내서 (자막)

@pytest.fixture

def db(request):

db = Database()

request.addfinalizer(db.close)

return db

def test_db(db):

db.insert('answer', 42)

assert db.get('answer') == 42

“픽스처”란 테스트에서 쓰이는상태가 일관적인 재료를 말하는데

Page 286: 파이썬 생존 안내서 (자막)

@pytest.fixture

def db(request):

db = Database()

request.addfinalizer(db.close)

return db

def test_db(db):

db.insert('answer', 42)

assert db.get('answer') == 42

이렇게 데코레이터로 픽스처를 정의해두면매개변수 이름을 이용해서

Page 287: 파이썬 생존 안내서 (자막)

@pytest.fixture

def db(request):

db = Database()

request.addfinalizer(db.close)

return db

def test_db(db):

db.insert('answer', 42)

assert db.get('answer') == 42

어느 테스트 케이스에서든쉽게 끌어다 쓸 수 있게 해줘요.

Page 288: 파이썬 생존 안내서 (자막)

def test_answer_pid(monkeypatch):

monkeypatch.setattr(time, 'time', answer)

assert time.time() == 42

monkeypatch라는 픽스처도기본으로 제공돼요.

Page 289: 파이썬 생존 안내서 (자막)

def test_answer_pid(monkeypatch):

monkeypatch.setattr(time, 'time', answer)

assert time.time() == 42

이것을 쓰면 이 테스트에 한해서다른 모듈의 기능 일부를 바꿔칠 수 있어요.

Page 290: 파이썬 생존 안내서 (자막)

def test_answer_pid(monkeypatch):

monkeypatch.setattr(time, 'time', answer)

assert time.time() == 42

시간을 다루거나 외부 API를 쓰는코드를 테스트할 때 굉장히 유용합니다.

Page 291: 파이썬 생존 안내서 (자막)

pytest-cov

pytest에도 역시플러그인이 무척 많은데

Page 292: 파이썬 생존 안내서 (자막)

pytest-cov

그 중 pytest-cov를 쓰면줄 단위 테스트 커버리지를 구할 수 있어요.

Page 293: 파이썬 생존 안내서 (자막)

❯ pytest test.py --cov=durango

---------- coverage ----------

TOTAL 42 42 100%

테스트 돌릴 때

Page 294: 파이썬 생존 안내서 (자막)

❯ pytest test.py --cov=durango

---------- coverage ----------

TOTAL 42 42 100%

커버리지 측정할모듈을 같이 적어주면

Page 295: 파이썬 생존 안내서 (자막)

❯ pytest test.py --cov=durango

---------- coverage ----------

TOTAL 42 42 100%

그 모듈에 총 몇 줄이 있고유닛테스트가 그 중에서

Page 296: 파이썬 생존 안내서 (자막)

❯ pytest test.py --cov=durango

---------- coverage ----------

TOTAL 42 42 100%

얼마나 건드렸는지정확하게 알 수 있죠.

Page 297: 파이썬 생존 안내서 (자막)

coverage 71%

저희는 이 방법으로 테스트 커버리지를지속적으로 관리해왔고

Page 298: 파이썬 생존 안내서 (자막)

coverage 71%

충분히 높은 건 아니지만약 71% 정도를 확보해둔 상태입니다.

Page 299: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

그 밖에도 테스트케이스가 실패했을 때

Page 300: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

몇 번은 재시도할 수 있게끔 해주는pytest-rerunfailures란 게 있는데

Page 301: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

보통 시간에 민감한 테스트케이스는미묘한 시간차로 실패할 때가 있거든요.

Page 302: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

이때 쓰면 좋아요.

Page 303: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

또 테스트 케이스들을 나눠서여러 스레드나 머신에서 병렬적으로 돌려주는

Page 304: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

pytest-xdist 같은유용한 플러그인이 많이 있으니까

Page 305: 파이썬 생존 안내서 (자막)

•pytest-rerunfailures

•pytest-xdist

이것저것 찾아보고함께 도입해보시면 좋을 것 같습니다.

Page 306: 파이썬 생존 안내서 (자막)

Fabric

서버 소스코드가 빌드를 통과하면적당한 머신에 자동으로 배포되는데

Page 307: 파이썬 생존 안내서 (자막)

Fabric

여기엔 Fabric를 이용하고 있어요.

Page 308: 파이썬 생존 안내서 (자막)

빌드머신

원격

원격

원격

원격

원격

원격

원격

원격

원격

명령어

보통 여러 원격 머신에한 번에 명령어를 날리려면

Page 309: 파이썬 생존 안내서 (자막)

빌드머신

원격

원격

원격

원격

원격

원격

원격

원격

원격

에이전트

명령어

명령어를 받아줄에이전트를 띄워 둬야 하는데요

Page 310: 파이썬 생존 안내서 (자막)

빌드머신

원격

원격

원격

원격

원격

원격

원격

원격

원격

명령어를 SSH로

보통은 sshd가 기본으로 깔려 있잖아요?

Page 311: 파이썬 생존 안내서 (자막)

빌드머신

원격

원격

원격

원격

원격

원격

원격

원격

원격

명령어를 SSH로

Fabric은 SSH를 명령 채널로 써서원격 머신에 추가적으로 아무것도 깔지 않아도

Page 312: 파이썬 생존 안내서 (자막)

빌드머신

원격

원격

원격

원격

원격

원격

원격

원격

원격

명령어를 SSH로

바로 명령어를 날릴 수 있어요.

Page 313: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

if files.exists('durango'):

run('cd durango')

run('git pull')

else:

run('git clone %s durango' % git_url)

run('cd durango')

Fabric으로 날리는 명령어는이렇게 파이썬 코드로 짜게 되는데

Page 314: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

if files.exists('durango'):

run('cd durango')

run('git pull')

else:

run('git clone %s durango' % git_url)

run('cd durango')

기본적으로는 파이썬 틀 안에쉘 명령어를 직접 써넣는 방식이에요.

Page 315: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

if files.exists('durango'):

run('cd durango')

run('git pull')

else:

run('git clone %s durango' % git_url)

run('cd durango')

배포 스크립트들을 짜다 보면꼭 이런 식으로

Page 316: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

if files.exists('durango'):

run('cd durango')

run('git pull')

else:

run('git clone %s durango' % git_url)

run('cd durango')

처음 돌린 건지 아닌지에 따라분기를 나누는 패턴이 많이 나타나는데

Page 317: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

if files.exists('durango'):

run('cd durango')

run('git pull')

else:

run('git clone %s durango' % git_url)

run('cd durango')

저희는 이런 귀찮음을 해소해주는도구도 쓰고 있어요.

Page 318: 파이썬 생존 안내서 (자막)

fabtools

바로 Fabric의 보조도구인fabtools입니다.

Page 319: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

require.git.working_copy(git_url, 'durango', update=True)

fabtools는 방금 본 것과 같은각종 예외처리 분기를

Page 320: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

require.git.working_copy(git_url, 'durango', update=True)

무엇무엇이 필요하다는 형태로추상화 해줘요.

Page 321: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

require.git.working_copy(git_url, 'durango', update=True)

이 코드는원격 머신이 어떤 상황이든 간에

Page 322: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

require.git.working_copy(git_url, 'durango', update=True)

Git 저장소가 필요하니알아서 만들어 달라는 뜻이에요.

Page 323: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

require.git.working_copy(git_url, 'durango', update=True)

fabtools 덕분에절차적이던 저희 배포 스크립트를

Page 324: 파이썬 생존 안내서 (자막)

@task

def setup_durango():

require.git.working_copy(git_url, 'durango', update=True)

선언적으로 만들 수 있게 됐죠.

Page 325: 파이썬 생존 안내서 (자막)

coverage 71%build passing

저희에게 지속적인 빌드와 배포는

Page 326: 파이썬 생존 안내서 (자막)

coverage 71%build passing

병합요청과 코드리뷰,그리고 QA 절차에 필수적이었어요.

Page 327: 파이썬 생존 안내서 (자막)

coverage 71%build passing

제가 소개한 도구들을적당한 CI에서 이용하면

Page 328: 파이썬 생존 안내서 (자막)

coverage 71%build passing

여러분의 개발환경을 개선하는 데에도도움이 될 것 같습니다.

Page 329: 파이썬 생존 안내서 (자막)

컨벤션 통일

Page 330: 파이썬 생존 안내서 (자막)

컨벤션 통일

다른 모든 언어와 마찬가지로

Page 331: 파이썬 생존 안내서 (자막)

컨벤션 통일

파이썬에서도 코딩 컨벤션을통일해두는 게

Page 332: 파이썬 생존 안내서 (자막)

컨벤션 통일

좋은 코드를 짜기에도 좋고나중에 유지보수 하기에도 편리해요.

Page 333: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

파이썬엔 표준 컨벤션이 있어요.

Page 334: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

PEP8이라는8번째 제안서에서 규정하고 있죠.

Page 335: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

하드탭 대신 4칸짜리 스페이스를써야 한다든가

Page 336: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

코드의 최대 폭이79칸을 넘지 말아야 한다든가

Page 337: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

import문 종류에 따른순서를 지켜야 한다든가

Page 338: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

snake_case나 PascalCase 등을용도에 맞춰서 써야 한다든가

Page 339: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

뭐 그런 식이에요.

Page 340: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

여기엔 아무리 파이썬에 익숙해져도

Page 341: 파이썬 생존 안내서 (자막)

PEP8

•4칸 소프트탭

•79칸 제한

•import 순서

•snake_case, PascalCase

손으로 일일이 맞추기는어려운 규칙도 많아요.

Page 342: 파이썬 생존 안내서 (자막)

Flake8

그래서 저희는 Flake8이라는파이썬 코드 품질 검사기를 쓰고 있어요.

Page 343: 파이썬 생존 안내서 (자막)

❯ flake8 bad.py

bad.py:1:1: F401 'sys' imported but unused

bad.py:2:1: F401 'os' imported but unused

bad.py:7:6: E111 indentation is not a multiple of four

이런 식으로 어떤 코드가 컨벤션에 안 맞는지아니면 잘못 쓰였는지

Page 344: 파이썬 생존 안내서 (자막)

❯ flake8 bad.py

bad.py:1:1: F401 'sys' imported but unused

bad.py:2:1: F401 'os' imported but unused

bad.py:7:6: E111 indentation is not a multiple of four

코드를 읽어서 정적으로 분석해주는단순한 Lint인데

Page 345: 파이썬 생존 안내서 (자막)

❯ flake8 bad.py

bad.py:1:1: F401 'sys' imported but unused

bad.py:2:1: F401 'os' imported but unused

bad.py:7:6: E111 indentation is not a multiple of four

이걸 작업자 각자가에디터에 연동해서 쓰기도 하고

Page 346: 파이썬 생존 안내서 (자막)

❯ flake8 bad.py

bad.py:1:1: F401 'sys' imported but unused

bad.py:2:1: F401 'os' imported but unused

bad.py:7:6: E111 indentation is not a multiple of four

CI에서 빌드 돌릴 때같이 돌리기도 합니다.

Page 347: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

Flake8에도 플러그인이 무척 많아서

Page 348: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

프로젝트 내부 컨벤션에도맞춰서 쓸 수 있어요.

Page 349: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

flake8-import-order는아까 얘기한 import문의

Page 350: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

종류별 순서 뿐 아니라ABC 순 정렬까지도 맞춰주고

Page 351: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

flake8-print는 정식 코드에print문이 들어가는 걸 막아주며

Page 352: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

flake8-commas는 list나 dict 등의리터럴을 여러 줄에 걸쳐서 쓸 때

Page 353: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

항상 마지막 콤마를 찍게끔강제해줘요.

Page 354: 파이썬 생존 안내서 (자막)

•flake8-import-order

•flake8-print

•flake8-commas

물론 이 외에도아주 많은 플러그인이 있죠.

Page 355: 파이썬 생존 안내서 (자막)

Vim+Syntastic|pymode

Vim 쓰시는 분 많죠?

Page 356: 파이썬 생존 안내서 (자막)

Vim+Syntastic|pymode

Vim 플러그인인Syntastic이나 pymode를 쓰면

Page 357: 파이썬 생존 안내서 (자막)

Vim+Syntastic|pymode

이렇게 에디터에서도실수를 바로바로 확인할 수 있어요.

Page 358: 파이썬 생존 안내서 (자막)

Vim+Syntastic|pymode

저는 이렇게 작업합니다.

Page 359: 파이썬 생존 안내서 (자막)

Emacs+Flycheck

저는 Vim파지만 저희 스튜디오에Emacs 쓰시는 분도 있는데

Page 360: 파이썬 생존 안내서 (자막)

Emacs+Flycheck

그분은 같은 용도로Flycheck라는 걸 쓴다고 해요.

Page 361: 파이썬 생존 안내서 (자막)

Emacs+Flycheck

마찬가지로 Flake8 결과를에디터에서 바로 볼 수 있어요.

Page 362: 파이썬 생존 안내서 (자막)

•PEP8

•The Pocoo Style Guide

PEP8은 파이썬 커뮤니티에선절대적이라서

Page 363: 파이썬 생존 안내서 (자막)

•PEP8

•The Pocoo Style Guide

특히 오픈소스 활동을 할 때는반드시 지키는 게 좋아요.

Page 364: 파이썬 생존 안내서 (자막)

•PEP8

•The Pocoo Style Guide

하지만 PEP8만으로는간혹 모호한 경우도 있는데

Page 365: 파이썬 생존 안내서 (자막)

•PEP8

•The Pocoo Style Guide

그래서 저희는 PEP8을 기반으로좀 더 구체적이고 실용적인 규칙을 제시하는

Page 366: 파이썬 생존 안내서 (자막)

•PEP8

•The Pocoo Style Guide

Pocoo 스타일을 따르고 있습니다.

Page 367: 파이썬 생존 안내서 (자막)

REPL

Page 368: 파이썬 생존 안내서 (자막)

REPL

저는 파이썬으로 코딩할 때REPL을 굉장히 많이 써요.

Page 369: 파이썬 생존 안내서 (자막)

REPL

REPL은Read-Evaluate-Print Loop의 약자인데

Page 370: 파이썬 생존 안내서 (자막)

REPL

터미널에서 인자 없이 python 치면 나오는대화형 인터프리터 콘솔 있잖아요?

Page 371: 파이썬 생존 안내서 (자막)

REPL

그런 걸 가리켜요.

Page 372: 파이썬 생존 안내서 (자막)

REPL

코드를 파일에 적어서실행하는 것도 물론 좋지만

Page 373: 파이썬 생존 안내서 (자막)

REPL

문제를 탐색해나가는REPL만의 방식에도 매력이 있습니다.

Page 374: 파이썬 생존 안내서 (자막)

❯ python

Python 2.7.11+ (default, Apr 17 2016, 14:00:29)

[GCC 5.3.1 20160413] on linux2

Type "help", "copyright", "credits" or "license"

for more information.

>>> import os

>>> os.getpid()

42

파이썬 기본 REPL은 다들 아실 테고

Page 375: 파이썬 생존 안내서 (자막)

❯ ipython

Python 2.7.11+ (default, Apr 17 2016, 14:00:29)

Type "copyright", "credits" or "license" for

more information.

In [1]: import os

In [2]: os.getpid()

Out[2]: 42

IPython이라는 조금 더 스마트한REPL도 있는데

Page 376: 파이썬 생존 안내서 (자막)

❯ ipython

Python 2.7.11+ (default, Apr 17 2016, 14:00:29)

Type "copyright", "credits" or "license" for

more information.

In [1]: import os

In [2]: os.getpid()

Out[2]: 42

문법강조도 되고 자동완성도 되고그 밖에도 다양한 편의기능이 들어있어요.

Page 377: 파이썬 생존 안내서 (자막)

❯ ipython

Python 2.7.11+ (default, Apr 17 2016, 14:00:29)

Type "copyright", "credits" or "license" for

more information.

In [1]: import os

In [2]: os.getpid()

Out[2]: 42

이 IPython을 터미널 대신웹에서도 쓸 수 있게 나온 게

Page 379: 파이썬 생존 안내서 (자막)

중간중간 문서도 집어넣을 수도 있고

Page 380: 파이썬 생존 안내서 (자막)

데이터를 터미널에서보다훨씬 풍부하게 시각화할 수 있어요.

Page 381: 파이썬 생존 안내서 (자막)

그래서 데이터 과학 쪽에선앞에서 소개했듯이

Page 382: 파이썬 생존 안내서 (자막)

탐색적 분석하는 데에이것을 많이 쓴다고 합니다.

Page 383: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

저희가 만드는 게 MMORPG라서 그런지

Page 384: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

간단한 게임플레이 테스트 시나리오하나 짜려고 해도

Page 385: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

그 시나리오에 필요한게임 세계를 구축하는데

Page 386: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

굉장히 깊은 의존성을풀어야 하는 경우가 있어요.

Page 387: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

이 코드는 읽으라고 쓴 게 아니에요.

Page 388: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

여기서 테스트하고 싶은 건

Page 389: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

그저 플레이어를 호수 근처로옮기는 것 뿐인데

Page 390: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

플레이어와 호수를 준비하려면

Page 391: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

앞쪽에 훨씬 복잡하고 많은 코드가필요할 수 있다는 걸 보여드리고 싶었어요.

Page 392: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

게임서버에서는 게임 세계가알아서 구축되잖아요?

Page 393: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

만약 서버를 직접 돌리는 방식으로만테스트하게 되면

Page 394: 파이썬 생존 안내서 (자막)

>>> world_id = db.find_all(World.id)[0]

>>> world = db.load(World, world_id)

>>> islands = world.all_islands()

>>> for island in islands:

... if 'Python' in island.name:

... break

... else:

... assert False, 'island not found'

...

>>> lakes = island.find_lakes()

>>> lake = lakes[42]

>>> p = Player(id=1)

>>> p.level = 0

>>> for item in INITIAL_ITEMS:

... p.inventory.add_item(item)

...

>>> homes = island.find_homes()

>>> p.home = homes[999]

>>> p.move_to(lake)

Player-1 moved to lake-42!

이런 부분이 고달플 수 있다는 걸잘 모르고 넘어갈 수 있어요.

Page 395: 파이썬 생존 안내서 (자막)

>>> lake = find_lake('*/42')

>>> p = make_player(1, mock=True)

>>> p.move_to(lake)

Player-1 moved to lake-42!

>>> p.is_nearby(lake)

True

하지만 REPL을 주로 쓰면자연스레 귀찮고 반복적인 의존성 풀기를

Page 396: 파이썬 생존 안내서 (자막)

>>> lake = find_lake('*/42')

>>> p = make_player(1, mock=True)

>>> p.move_to(lake)

Player-1 moved to lake-42!

>>> p.is_nearby(lake)

True

최대한 쉽게 쓸 수 있게끔추상화하게 되곤 합니다.

Page 397: 파이썬 생존 안내서 (자막)

>>> lake = find_lake('*/42')

>>> p = make_player(1, mock=True)

>>> p.move_to(lake)

Player-1 moved to lake-42!

>>> p.is_nearby(lake)

True

그래야 REPL에서 다시 쓰기 편하니까요.

Page 398: 파이썬 생존 안내서 (자막)

>>> lake = find_lake('*/42')

>>> p = make_player(1, mock=True)

>>> p.move_to(lake)

Player-1 moved to lake-42!

>>> p.is_nearby(lake)

True

이런 식으로 REPL에서한 번 돌려본 명령어들을

Page 399: 파이썬 생존 안내서 (자막)

def test_move_to():

lake = find_lake('*/42')

p = make_player(1, mock=True)

p.move_to(lake)

assert p.is_nearby(lake)

복붙해서 테스트케이스로 구워 두면두고두고 도움 될 거예요.

Page 400: 파이썬 생존 안내서 (자막)

def test_move_to():

lake = find_lake('*/42')

p = make_player(1, mock=True)

p.move_to(lake)

assert p.is_nearby(lake)

이렇듯 우리가 만드는 프로그램을유저로서 테스트하는 데에 그치지 말고

Page 401: 파이썬 생존 안내서 (자막)

def test_move_to():

lake = find_lake('*/42')

p = make_player(1, mock=True)

p.move_to(lake)

assert p.is_nearby(lake)

REPL을 주요 개발 도구 중 하나로 삼아서

Page 402: 파이썬 생존 안내서 (자막)

def test_move_to():

lake = find_lake('*/42')

p = make_player(1, mock=True)

p.move_to(lake)

assert p.is_nearby(lake)

API를 직접,그리고 또 자주 써보는 게

Page 403: 파이썬 생존 안내서 (자막)

def test_move_to():

lake = find_lake('*/42')

p = make_player(1, mock=True)

p.move_to(lake)

assert p.is_nearby(lake)

API를 더 낫게 만드는 데도움된다고 생각합니다.

Page 404: 파이썬 생존 안내서 (자막)

디버깅

Page 405: 파이썬 생존 안내서 (자막)

디버깅

이번엔 파이썬 코드를디버깅하는 방법이에요.

Page 406: 파이썬 생존 안내서 (자막)

Pdb

파이썬 표준 라이브러리엔 Pdb라고gdb 같은 콘솔 디버거가 들어있어요.

Page 407: 파이썬 생존 안내서 (자막)

def hello_world():

hello = 'world'

pdb.set_trace()

return hello

이렇게 코드 사이에서pdb.set_trace()를 불러주면

Page 408: 파이썬 생존 안내서 (자막)

def hello_world():

hello = 'world'

pdb.set_trace()

return hello

다른 IDE에서 많이 보셨을브레이크포인트가 걸려요.

Page 409: 파이썬 생존 안내서 (자막)

> helloworld.py(7)hello_world()

-> return hello

(Pdb) hello

'world'

(Pdb) l

4 def hello_world():

5 hello = 'world'

6 pdb.set_trace()

7 -> return hello

8 hello_world()

[EOF]

실행 도중에 브레이크포인트에 걸리면디버깅 콘솔이 뜨는데

Page 410: 파이썬 생존 안내서 (자막)

> helloworld.py(7)hello_world()

-> return hello

(Pdb) hello

'world'

(Pdb) l

4 def hello_world():

5 hello = 'world'

6 pdb.set_trace()

7 -> return hello

8 hello_world()

[EOF]

보통의 IDE 디버거와 마찬가지로

Page 411: 파이썬 생존 안내서 (자막)

> helloworld.py(7)hello_world()

-> return hello

(Pdb) hello

'world'

(Pdb) l

4 def hello_world():

5 hello = 'world'

6 pdb.set_trace()

7 -> return hello

8 hello_world()

[EOF]

변수 내용을 확인한다거나

Page 412: 파이썬 생존 안내서 (자막)

> helloworld.py(7)hello_world()

-> return hello

(Pdb) hello

'world'

(Pdb) l

4 def hello_world():

5 hello = 'world'

6 pdb.set_trace()

7 -> return hello

8 hello_world()

[EOF]

실행중인 코드 위치를 확인한다거나

Page 413: 파이썬 생존 안내서 (자막)

> helloworld.py(7)hello_world()

-> return hello

(Pdb) hello

'world'

(Pdb) l

4 def hello_world():

5 hello = 'world'

6 pdb.set_trace()

7 -> return hello

8 hello_world()

[EOF]

아니면 콜스택 위아래를 오간다거나한 스텝씩 넘겨본다거나 할 수 있어요.

Page 414: 파이썬 생존 안내서 (자막)

> helloworld.py(7)hello_world()

-> return hello

(Pdb++) hello

'world'

(Pdb++) l

4 def hello_world():

5 hello = 'world'

6 pdb.set_trace()

7 -> return hello

8 hello_world()

[EOF]

pdb++ 확장을 깔면문법강조도 볼 수 있죠.

Page 415: 파이썬 생존 안내서 (자막)

>>> x = 0

>>> x / x

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: ...

>>> pdb.pm()

> <stdin>(1)<module>()

(Pdb) x

0

Pdb는 REPL에서 놀다가에러 났을 때도 유용하게 쓸 수 있어요.

Page 416: 파이썬 생존 안내서 (자막)

>>> x = 0

>>> x / x

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: ...

>>> pdb.pm()

> <stdin>(1)<module>()

(Pdb) x

0

pdb.pm(), “부검”을 뜻하는“Post-mortem”의 약자죠.

Page 417: 파이썬 생존 안내서 (자막)

>>> x = 0

>>> x / x

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: ...

>>> pdb.pm()

> <stdin>(1)<module>()

(Pdb) x

0

이것을 실행하면 마지막 에러가 발생했던 곳을바로 디버깅할 수 있어요.

Page 418: 파이썬 생존 안내서 (자막)

>>> x = 0

>>> x / x

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: ...

>>> pdb.pm()

> <stdin>(1)<module>()

(Pdb) x

0

저희 서버도 디버깅 용으로 돌리면서버 실행 도중 pdb.pm()으로

Page 419: 파이썬 생존 안내서 (자막)

>>> x = 0

>>> x / x

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: ...

>>> pdb.pm()

> <stdin>(1)<module>()

(Pdb) x

0

마지막 에러를 디버깅해볼 수 있게연동해놨어요.

Page 420: 파이썬 생존 안내서 (자막)

>>> x = 0

>>> x / x

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: ...

>>> pdb.pm()

> <stdin>(1)<module>()

(Pdb) x

0

다만 디버깅 콘솔 사용법이처음엔 조금 생소할 수 있는데

Page 421: 파이썬 생존 안내서 (자막)

(Pdb) ?

Documented commands (type help <topic>):

========================================

EOF bt cont enable jump pp run unt

a c continue exit l q s until

alias cl d h list quit step up

args clear debug help n r tbreak w

b commands disable ignore next restart u whatis

break condition down j p return unalias where

Miscellaneous help topics:

==========================

exec pdb

물음표 치면 설명서가 나오니까

Page 422: 파이썬 생존 안내서 (자막)

(Pdb) ?

Documented commands (type help <topic>):

========================================

EOF bt cont enable jump pp run unt

a c continue exit l q s until

alias cl d h list quit step up

args clear debug help n r tbreak w

b commands disable ignore next restart u whatis

break condition down j p return unalias where

Miscellaneous help topics:

==========================

exec pdb

조금씩 찾아보면서 익히다 보면아마 금방 익숙해질 수 있을 거예요.

Page 423: 파이썬 생존 안내서 (자막)

PTVS

저희 스튜디오에서비주얼스튜디오를 선호하시는 분들은

Page 424: 파이썬 생존 안내서 (자막)

PTVS

마이크로소프트에서 만든파이썬 개발 플러그인인 PTVS를 씁니다.

Page 425: 파이썬 생존 안내서 (자막)

PTVS 디버거도 굉장히 훌륭해서

Page 426: 파이썬 생존 안내서 (자막)

서버에 에이전트를같이 띄워주기만 하면

Page 427: 파이썬 생존 안내서 (자막)

비주얼스튜디오 IDE를 한껏 활용하면서GUI로 편하게 디버깅할 수 있어요.

Page 428: 파이썬 생존 안내서 (자막)

print(strange_value)

이렇듯 파이썬에서 print로만디버깅할 수 있는 건 아니니까

Page 429: 파이썬 생존 안내서 (자막)

print(strange_value)

여러가지 편한 도구를 찾아서사용해보시면 좋겠습니다.

Page 430: 파이썬 생존 안내서 (자막)

자료구조

Page 431: 파이썬 생존 안내서 (자막)

자료구조

프로그램 만들 땐 언제나가장 적절한 자료구조를 골라야 하죠.

Page 432: 파이썬 생존 안내서 (자막)

자료구조

그래야 올바른 코드를 짤 수 있고또 성능을 높이는 데도 도움 되니까요.

Page 433: 파이썬 생존 안내서 (자막)

자료구조

list나 set 같은 빌트인 자료구조는이미 다 아실 테니까

Page 434: 파이썬 생존 안내서 (자막)

자료구조

그 밖의 유용한파이썬 자료구조를 소개해볼게요.

Page 435: 파이썬 생존 안내서 (자막)

collections

우선 collections표준 라이브러리부터 살펴볼게요.

Page 436: 파이썬 생존 안내서 (자막)

list.insert(0, x)

deque.appendleft(x)

collections 안에 있는 deque은

Page 437: 파이썬 생존 안내서 (자막)

list.insert(0, x)

deque.appendleft(x)

상수시간에 앞뒤 양 끝 요소를조작할 수 있는 자료구조예요.

Page 438: 파이썬 생존 안내서 (자막)

list.insert(0, x)

deque.appendleft(x)

반면 list는 맨 뒤가 아니면상수시간에 추가하거나 뺄 수 없죠.

Page 439: 파이썬 생존 안내서 (자막)

stack = []

while f:

stack.append(f)

f = f.f_back

stack.reverse()

stack = deque()

while f:

stack.appendleft(f)

이런 식으로 list에 순차적으로 쌓은 다음뒤집는 경우가 있는데

Page 440: 파이썬 생존 안내서 (자막)

stack = []

while f:

stack.append(f)

f = f.f_back

stack.reverse()

stack = deque()

while f:

stack.appendleft(f)

deque을 쓰면 처음부터역순으로 쌓을 수 있어요.

Page 441: 파이썬 생존 안내서 (자막)

>>> sub = OrderedDict([('S', 1), ('U', 2), ('B', 3)])

>>> sub['S']

1

>>> sub.keys()

['S', 'U', 'B']

OrderedDict는 dict의 서브클래스인데그냥 dict와 달리 순서를 보장해줘서

Page 442: 파이썬 생존 안내서 (자막)

>>> sub = OrderedDict([('S', 1), ('U', 2), ('B', 3)])

>>> sub['S']

1

>>> sub.keys()

['S', 'U', 'B']

순서도 중요하고키로 접근하는 것도 중요할 때 쓰면 좋아요.

Page 443: 파이썬 생존 안내서 (자막)

>>> Vector2 = namedtuple('Vector2', ['x', 'y'])

>>> xy = Vector2(20, 59)

>>> xy.x

20

>>> x, y = xy

namedtuple은 tuple의 서브클래스를만들어주는 함수인데

Page 444: 파이썬 생존 안내서 (자막)

>>> Vector2 = namedtuple('Vector2', ['x', 'y'])

>>> xy = Vector2(20, 59)

>>> xy.x

20

>>> x, y = xy

이때 각 원소에이름을 붙일 수 있어요.

Page 445: 파이썬 생존 안내서 (자막)

>>> Vector2 = namedtuple('Vector2', ['x', 'y'])

>>> xy = Vector2(20, 59)

>>> xy.x

20

>>> x, y = xy

이것으로 만든 클래스도 tuple 처럼 불변이고다중대입문에도 쓸 수 있죠.

Page 446: 파이썬 생존 안내서 (자막)

>>> Vector2 = namedtuple('Vector2', ['x', 'y'])

>>> xy = Vector2(20, 59)

>>> xy.x

20

>>> x, y = xy

저는 사소한 클래스 만들 땐class문을 직접 쓰는 대신

Page 447: 파이썬 생존 안내서 (자막)

>>> Vector2 = namedtuple('Vector2', ['x', 'y'])

>>> xy = Vector2(20, 59)

>>> xy.x

20

>>> x, y = xy

이 쪽을 더 선호하는 편이에요.

Page 448: 파이썬 생존 안내서 (자막)

>>> c = Counter('nexon')

>>> c['n']

2

>>> c['x']

1

Counter는 dict의 서브클래스고이렇게 뭔가 간단히 셀 때 쓰기 좋아요.

Page 449: 파이썬 생존 안내서 (자막)

>>> d = defaultdict(int)

>>> d['yo']

0

defaultdict는 없는 키에 접근했을 때KeyError를 내는 대신

Page 450: 파이썬 생존 안내서 (자막)

>>> d = defaultdict(int)

>>> d['yo']

0

팩토리함수 실행 결과를채워주는 dict예요.

Page 451: 파이썬 생존 안내서 (자막)

def inf_dict():

return defaultdict(inf_dict)

이건 파이콘 KR 2014구종만 님 발표에서 봤던 코드인데

Page 452: 파이썬 생존 안내서 (자막)

def inf_dict():

return defaultdict(inf_dict)

defaultdict 만드는 함수 자체를defaultdict의 팩토리함수로 쓰고 있죠?

Page 453: 파이썬 생존 안내서 (자막)

root = inf_dict()

root[0][1][2][3][4][5][6][7] = 'So deep!'

그래서 아무 키나끊임 없이 써내려 갈 수 있어요.

Page 454: 파이썬 생존 안내서 (자막)

써드파티

collections에만 해도유용한 자료구조가 많이 있지만

Page 455: 파이썬 생존 안내서 (자막)

써드파티

써드파티에는 더 재밌는 게 많아요.

Page 456: 파이썬 생존 안내서 (자막)

foo = [1, 10, 100, 1000]

x = bisect_left(foo, 42)

foo.insert(x, 42)

from sortedcontainer import SortedList

bar = SortedList([1, 10, 100, 1000])

bar.add(42)

sortedcontainers는항상 정렬돼있는 list와 dict를 제공해서

Page 457: 파이썬 생존 안내서 (자막)

foo = [1, 10, 100, 1000]

x = bisect_left(foo, 42)

foo.insert(x, 42)

from sortedcontainer import SortedList

bar = SortedList([1, 10, 100, 1000])

bar.add(42)

굉장히 빠르게 원소를 정렬된 상태로넣거나 빼는 데 쓸 수 있어요.

Page 458: 파이썬 생존 안내서 (자막)

foo = [1, 10, 100, 1000]

x = bisect_left(foo, 42)

foo.insert(x, 42)

from sortedcontainer import SortedList

bar = SortedList([1, 10, 100, 1000])

bar.add(42)

표준 list나 set으로는할 수 없는 일이죠.

Page 459: 파이썬 생존 안내서 (자막)

>>> from bidict import bidict

>>> type_table = bidict()

>>> type_table[1] = Ping

>>> type_table[2] = Pong

>>> type_table.inv[Pong]

2

bidict는 양방향 dict로,

Page 460: 파이썬 생존 안내서 (자막)

>>> from bidict import bidict

>>> type_table = bidict()

>>> type_table[1] = Ping

>>> type_table[2] = Pong

>>> type_table.inv[Pong]

2

키로 값을 찾는 것 뿐만 아니라값으로 키를 찾는 것까지 가능해서

Page 461: 파이썬 생존 안내서 (자막)

>>> from bidict import bidict

>>> type_table = bidict()

>>> type_table[1] = Ping

>>> type_table[2] = Pong

>>> type_table.inv[Pong]

2

타입코드 테이블 같은 거만들 때 유용해요.

Page 462: 파이썬 생존 안내서 (자막)

from cachetools import LRUCache

cache = LRUCache(256)

cachetools에는LRU나 TTL을 비롯한

Page 463: 파이썬 생존 안내서 (자막)

from cachetools import LRUCache

cache = LRUCache(256)

여러가지 캐싱 정책을 구현한dict의 서브클래스들이 들어있어요.

Page 464: 파이썬 생존 안내서 (자막)

from cachetools import LRUCache

cache = LRUCache(256)

서버에 캐싱을 구현할 일이 종종 있는데그럴 때 쓰기 편하더라고요.

Page 465: 파이썬 생존 안내서 (자막)

namedtuple, deque, OrderedDict,

SortedList, bidict, LRUCache, ...

이렇게 빌트인 말고도 여러가지 자료구조를라이브러리에서 구할 수 있으니까

Page 466: 파이썬 생존 안내서 (자막)

namedtuple, deque, OrderedDict,

SortedList, bidict, LRUCache, ...

각각의 용도와 특성을 파악해서더 효율적인 코드를 쉽게 짜는 데 써보세요.

Page 467: 파이썬 생존 안내서 (자막)

제너레이터

Page 468: 파이썬 생존 안내서 (자막)

제너레이터

파이썬에는 함수 실행을 도중에 멈췄다가재개시킬 수 있는 제너레이터가 있죠.

Page 469: 파이썬 생존 안내서 (자막)

제너레이터

앞서 소개한 asyncio의재료가 되기도 했고요.

Page 470: 파이썬 생존 안내서 (자막)

제너레이터

C# 쓰시는 분들은이미 익숙하실 거예요.

Page 471: 파이썬 생존 안내서 (자막)

def only_odds(nums):

for n in nums:

if n % 2 == 1:

yield n

함수 중간에 return 대신 yield를 넣으면그 지점에서 함수 실행이 중단돼요.

Page 472: 파이썬 생존 안내서 (자막)

>>> odds = only_odds(range(100))

>>> next(odds)

1

>>> next(odds)

3

호출한 쪽에선yield된 값을 받아올 수 있고

Page 473: 파이썬 생존 안내서 (자막)

>>> odds = only_odds(range(100))

>>> next(odds)

1

>>> next(odds)

3

next()를 이용해서다음 이터레이션으로 넘어갈 수 있어요.

Page 474: 파이썬 생존 안내서 (자막)

def only_odds(nums):

for n in nums:

if n % 2 == 1:

yield nnext()

next()

next()

next()

next()

이런 식으로 next()하는 만큼함수가 실행되게 돼서

Page 475: 파이썬 생존 안내서 (자막)

def only_odds(nums):

for n in nums:

if n % 2 == 1:

yield nnext()

next()

next()

next()

next()

레이지 이터레이터를아주 쉽게 구현할 수 있죠.

Page 476: 파이썬 생존 안내서 (자막)

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

특히 한 시퀀스를여러 번 변형하는 경우

Page 477: 파이썬 생존 안내서 (자막)

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

제너레이터를 쓰면 실제 이터레이션을한 바퀴로 줄일 수 있는데

Page 478: 파이썬 생존 안내서 (자막)

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

이 코드는0부터 999,999까지의 숫자 중에서

Page 479: 파이썬 생존 안내서 (자막)

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

홀수만 취한 다음

Page 480: 파이썬 생존 안내서 (자막)

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

그 제곱을 구하는 코드입니다.

Page 481: 파이썬 생존 안내서 (자막)

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

세 줄 모두 이터레이터나제너레이터만 만들고 있고

Page 482: 파이썬 생존 안내서 (자막)

>>> seq

<generator object <genexpr>>

실제로 이터레이션을 돌리진 않아요.

Page 483: 파이썬 생존 안내서 (자막)

>>> list(seq)

[1, 9, 25, 49, 81, 121, ...]

결과를 보기 위해 list 등으로 감싸면그때야 비로소 이터레이션이 돌죠.

Page 484: 파이썬 생존 안내서 (자막)

>>> list(seq)

[1, 9, 25, 49, 81, 121, ...]

레이지하지 않았다면 이터레이션이250만 바퀴 돌았을 로직인데

Page 485: 파이썬 생존 안내서 (자막)

>>> list(seq)

[1, 9, 25, 49, 81, 121, ...]

이 방법으로딱 100만 바퀴만 돌 수 있게 됩니다.

Page 486: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'from exm import only_odds' '

seq = xrange(1000000)

seq = only_odds(seq)

seq = (x ** 2 for x in seq)

list(seq)

'

10 loops, best of 3: 137 msec per loop

간단히 벤치마킹해보니까제너레이터를 쓸 땐 137ms가 걸렸어요.

Page 487: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'from exm import only_odds' '

seq = list(xrange(1000000))

seq = list(only_odds(seq))

seq = [x ** 2 for x in seq]

'

10 loops, best of 3: 456 msec per loop

반면 매번 이터레이션하는 방식으로는456ms나 걸리는 걸 볼 수 있었습니다.

Page 488: 파이썬 생존 안내서 (자막)

>>> list(gen)

[1, 2, 3]

>>> list(gen)

[]

def gen():

yield 1

yield 2

yield 3

제너레이터는 딱 한 바퀴만돌릴 수 있어서

Page 489: 파이썬 생존 안내서 (자막)

>>> list(gen)

[1, 2, 3]

>>> list(gen)

[]

def gen():

yield 1

yield 2

yield 3

여러 번 돌려야 하는 경우에당황스러울 수 있어요.

Page 490: 파이썬 생존 안내서 (자막)

seq = list(gen)

do_something(seq)

do_another(seq)

이럴 땐 list 등으로미리 한번 평가해놓고 쓰거나

Page 491: 파이썬 생존 안내서 (자막)

gen1, gen2 = itertools.tee(gen)

do_something(gen1)

do_another(gen2)

아니면 표준 라이브러리itertools에 있는 tee를 이용해서

Page 492: 파이썬 생존 안내서 (자막)

gen1, gen2 = itertools.tee(gen)

do_something(gen1)

do_another(gen2)

제너레이터를 여러 개의 뷰로나눠서 쓸 수 있어요.

Page 493: 파이썬 생존 안내서 (자막)

gen1, gen2 = itertools.tee(gen)

do_something(gen1)

do_another(gen2)

tee는 리눅스 커맨드에서 따온 이름인데T자형 파이프를 뜻합니다.

Page 494: 파이썬 생존 안내서 (자막)

enemy = player.nearest_player()

while player.alive() or enemy.alive():

player.move_to(enemy)

player.attack(enemy)

yield

제너레이터는 AI 만들 때쓰기에도 굉장히 편리한데

Page 495: 파이썬 생존 안내서 (자막)

enemy = player.nearest_player()

while player.alive() or enemy.alive():

player.move_to(enemy)

player.attack(enemy)

yield

지금이 어떤 상태고각종 정보를 다음 틱에 어떻게 넘길지

Page 496: 파이썬 생존 안내서 (자막)

enemy = player.nearest_player()

while player.alive() or enemy.alive():

player.move_to(enemy)

player.attack(enemy)

yield

고민할 필요 없이그냥 지역변수를 쓰면 되거든요.

Page 497: 파이썬 생존 안내서 (자막)

if self.state is not Fighting:

self.enemy = player.nearest_player()

self.state = Fighting

elif player.alive() or self.enemy.alive():

player.move_to(self.enemy)

player.attack(self.enemy)

제너레이터가 없다면어떤 형태로든 스테이트머신이 필요하겠죠?

Page 498: 파이썬 생존 안내서 (자막)

yield

파이썬에서 yield는raise나 return만큼

Page 499: 파이썬 생존 안내서 (자막)

yield

많이 쓰이는 제어구문이고그만큼 쓰기도 쉽습니다.

Page 500: 파이썬 생존 안내서 (자막)

yield

제너레이터로 이터레이션을 줄일 수 있거나복잡도를 낮출 수 있는 곳이 없는지

Page 501: 파이썬 생존 안내서 (자막)

yield

한 번 확인해보시면 좋을 것 같아요.

Page 502: 파이썬 생존 안내서 (자막)

성능 측정

Page 503: 파이썬 생존 안내서 (자막)

성능 측정

프로그램을 최적화하려면일단 성능부터 제대로 측정해야 합니다.

Page 504: 파이썬 생존 안내서 (자막)

성능 측정

이번엔 저희가 사용하는 몇 가지성능 측정 방법을 소개해 볼게요.

Page 505: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 한 번 실행할 코드 벤치마킹할 코드

timeit은 표준 라이브러리예요.

Page 506: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 한 번 실행할 코드 벤치마킹할 코드

앞에서 제너레이터 벤치마킹할 때이것을 썼었죠.

Page 507: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 한 번 실행할 코드 벤치마킹할 코드

그냥 쉘에다가 이렇게 치면

Page 508: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 한 번 실행할 코드 벤치마킹할 코드

적당히 너무 오래 걸리지 않는 선에서반복실행한 다음

Page 509: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 한 번 실행할 코드 벤치마킹할 코드

한 번 돌릴 때 실행시간이얼마나 걸리는지 알려줍니다.

Page 510: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'import math' 'math.log10(99999999)'

10000000 loops, best of 3: 0.108 usec per loop

이런 식으로요.

Page 511: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'import math' 'math.log10(99999999)'

10000000 loops, best of 3: 0.108 usec per loop

이건 math.log10()을벤치마킹하는 모습이에요.

Page 512: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'import math' 'math.log10(99999999)'

10000000 loops, best of 3: 0.108 usec per loop

여기서 math 모듈을 import하는 부분은벤치마킹 대상이 아니잖아요?

Page 513: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'import math' 'math.log10(99999999)'

10000000 loops, best of 3: 0.108 usec per loop

그래서 -s 옵션에 넣어서한 번만 부르게 했어요.

Page 514: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'import math' 'math.log10(99999999)'

10000000 loops, best of 3: 0.108 usec per loop

1,000만 번 씩 3번 돌려봤고

Page 515: 파이썬 생존 안내서 (자막)

❯ python -m timeit \

-s 'import math' 'math.log10(99999999)'

10000000 loops, best of 3: 0.108 usec per loop

그 중 가장 빨랐을 때 log10 한 번에0.108μs가 걸렸다는 내용입니다.

Page 516: 파이썬 생존 안내서 (자막)

•호출 횟수

•고유 실행시간

•하위 콜스택 포함 실행시간

❯ python -m profile 파이썬 파일

profile 역시 표준 라이브러리인데

Page 517: 파이썬 생존 안내서 (자막)

•호출 횟수

•고유 실행시간

•하위 콜스택 포함 실행시간

❯ python -m profile 파이썬 파일

함수에 진입하고 나가는 이벤트를모두 기록해서

Page 518: 파이썬 생존 안내서 (자막)

•호출 횟수

•고유 실행시간

•하위 콜스택 포함 실행시간

❯ python -m profile 파이썬 파일

함수 별로 호출 횟수와실행시간을 측정해주죠.

Page 519: 파이썬 생존 안내서 (자막)

•호출 횟수

•고유 실행시간

•하위 콜스택 포함 실행시간

❯ python -m cProfile 파이썬 파일

표준 라이브러리에 cProfile이라고C로 만들어서 오버헤드가 적은 버전도 있으니까

Page 520: 파이썬 생존 안내서 (자막)

•호출 횟수

•고유 실행시간

•하위 콜스택 포함 실행시간

❯ python -m cProfile 파이썬 파일

저희처럼 PyPy가 아닌CPython을 쓰신다면 이걸 쓰세요.

Page 521: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

측정 결과는이런 식으로 출력되는데

Page 522: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

함수 별 호출 횟수와

Page 523: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

함수 자체에서 소비된 시간,

Page 524: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

여기에 하위 콜스택에서소비된 시간은 포함되지 않고

Page 525: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

하위 콜스택까지 포함한 시간은그 옆에 있어요.

Page 526: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

프로그램 덩치가 커지면측정 결과를 읽기 좀 힘들어지긴 하지만

Page 527: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

파이썬에서 가장 기본적으로 사용할 수 있는꽤 쓸만한 프로파일러입니다.

Page 528: 파이썬 생존 안내서 (자막)

❯ python -m cProfile log10.py

10000002 function calls in 2.269 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)

1 0.327 0.327 2.269 2.269 log10.py:1(<module>)

10000000 1.942 0.000 1.942 0.000 {math.log10}

단 측정 오버헤드가 제법 있으니까라이브 서버 같은 곳에선 쓰지 마세요.

Page 529: 파이썬 생존 안내서 (자막)

+ 스레드 별 측정

❯ python -m yappi 파이썬 파일

Yappi는 써드파티 라이브러리고요

Page 530: 파이썬 생존 안내서 (자막)

+ 스레드 별 측정

❯ python -m yappi 파이썬 파일

앞서 보신 profile과 비슷한데스레드 별 성능까지 측정해줘요.

Page 531: 파이썬 생존 안내서 (자막)

+ 스레드 별 측정

❯ python -m yappi 파이썬 파일

C로 구현돼있고아쉽게도 PyPy에선 쓸 수 없어요.

Page 532: 파이썬 생존 안내서 (자막)

❯ python -m yappi log10.py

Clock type: CPUOrdered by: totaltime, desc

name ncall tsub ttot tavglog10.py:1 <module> 1 0.000113 0.000415 0.000415log10.py:4 f 2 0.000000 0.000000 0.000000

name id tid ttot scntThread 1 140376132327168 0.029194 1Thread 2 140376123934464 0.029179 1_MainThread 0 140376158828288 0.004055 2

마찬가지로

Page 533: 파이썬 생존 안내서 (자막)

❯ python -m yappi log10.py

Clock type: CPUOrdered by: totaltime, desc

name ncall tsub ttot tavglog10.py:1 <module> 1 0.000113 0.000415 0.000415log10.py:4 f 2 0.000000 0.000000 0.000000

name id tid ttot scntThread 1 140376132327168 0.029194 1Thread 2 140376123934464 0.029179 1_MainThread 0 140376158828288 0.004055 2

함수 별 호출 횟수와

Page 534: 파이썬 생존 안내서 (자막)

❯ python -m yappi log10.py

Clock type: CPUOrdered by: totaltime, desc

name ncall tsub ttot tavglog10.py:1 <module> 1 0.000113 0.000415 0.000415log10.py:4 f 2 0.000000 0.000000 0.000000

name id tid ttot scntThread 1 140376132327168 0.029194 1Thread 2 140376123934464 0.029179 1_MainThread 0 140376158828288 0.004055 2

실행시간이 나오고

Page 535: 파이썬 생존 안내서 (자막)

❯ python -m yappi log10.py

Clock type: CPUOrdered by: totaltime, desc

name ncall tsub ttot tavglog10.py:1 <module> 1 0.000113 0.000415 0.000415log10.py:4 f 2 0.000000 0.000000 0.000000

name id tid ttot scntThread 1 140376132327168 0.029194 1Thread 2 140376123934464 0.029179 1_MainThread 0 140376158828288 0.004055 2

스레드 별 실행시간과스케줄링 횟수까지 알 수 있습니다.

Page 536: 파이썬 생존 안내서 (자막)

❯ python -m yappi log10.py

Clock type: CPUOrdered by: totaltime, desc

name ncall tsub ttot tavglog10.py:1 <module> 1 0.000113 0.000415 0.000415log10.py:4 f 2 0.000000 0.000000 0.000000

name id tid ttot scntThread 1 140376132327168 0.029194 1Thread 2 140376123934464 0.029179 1_MainThread 0 140376158828288 0.004055 2

게다가 약간만 커스터마이징 하면gevent 스레드도 측정할 수 있어요.

Page 537: 파이썬 생존 안내서 (자막)

+ 콜스택

+ 실시간

+ 원격

+ 샘플링

+ 대화형 TUI.

❯ profiling 파이썬 파일

Profiling은 저희 스튜디오에서 만든오픈소스 프로파일러예요.

Page 538: 파이썬 생존 안내서 (자막)

+ 콜스택

+ 실시간

+ 원격

+ 샘플링

+ 대화형 TUI.

❯ profiling 파이썬 파일

앞에서 보신 측정 결과들에는함수 목록이 1차원적으로만 나열돼 있었는데

Page 539: 파이썬 생존 안내서 (자막)

+ 콜스택

+ 실시간

+ 원격

+ 샘플링

+ 대화형 TUI.

❯ profiling 파이썬 파일

저희는 콜스택 계층구조까지보고싶어서 만들었어요.

Page 540: 파이썬 생존 안내서 (자막)

+ 콜스택

+ 실시간

+ 원격

+ 샘플링

+ 대화형 TUI.

❯ profiling 파이썬 파일

또 라이브 서버도 측정해보고 싶어서

Page 541: 파이썬 생존 안내서 (자막)

+ 콜스택

+ 실시간

+ 원격

+ 샘플링

+ 대화형 TUI.

❯ profiling 파이썬 파일

실시간 원격 성능측정 프로토콜과샘플링 프로파일링 기법,

Page 542: 파이썬 생존 안내서 (자막)

+ 콜스택

+ 실시간

+ 원격

+ 샘플링

+ 대화형 TUI.

❯ profiling 파이썬 파일

그리고 아까 봤던 Urwid를 이용해서대화형 TUI도 만들었습니다.

Page 543: 파이썬 생존 안내서 (자막)

이렇게 펼쳤다 접었다 왔다 갔다 하면서성능을 측정할 수 있어요.

Page 544: 파이썬 생존 안내서 (자막)

https://github.com/what-studio/profiling

저희 스튜디오 GitHub에 공개돼있으니까많이들 참여해주세요.

Page 545: 파이썬 생존 안내서 (자막)

https://github.com/what-studio/profiling

특히 별을 찍어주시면감사하겠습니다.

Page 546: 파이썬 생존 안내서 (자막)

•line_profiler줄 단위로 성능 측정

•memory_profiler줄 단위로 메모리 사용량과 변화량 측정

이밖에도 여러가지 프로파일러가 더 있는데

Page 547: 파이썬 생존 안내서 (자막)

•line_profiler줄 단위로 성능 측정

•memory_profiler줄 단위로 메모리 사용량과 변화량 측정

함수가 아닌 코드 줄 단위로성능을 측정해주는 line_profiler나

Page 548: 파이썬 생존 안내서 (자막)

•line_profiler줄 단위로 성능 측정

•memory_profiler줄 단위로 메모리 사용량과 변화량 측정

줄 단위로 메모리 사용량이얼마나 늘고 줄었는지 측정해주는

Page 549: 파이썬 생존 안내서 (자막)

•line_profiler줄 단위로 성능 측정

•memory_profiler줄 단위로 메모리 사용량과 변화량 측정

memory_profiler 같은 것도 있어요.

Page 550: 파이썬 생존 안내서 (자막)

•line_profiler줄 단위로 성능 측정

•memory_profiler줄 단위로 메모리 사용량과 변화량 측정

알아 두시면 언젠가쓸모가 있을 것 같습니다.

Page 551: 파이썬 생존 안내서 (자막)

로드타임

Page 552: 파이썬 생존 안내서 (자막)

로드타임

파이썬엔 컴파일타임이 없어서컴파일타임 최적화 같은 건 할 수 없지만

Page 553: 파이썬 생존 안내서 (자막)

로드타임

모듈을 로드하는 로드타임에

Page 554: 파이썬 생존 안내서 (자막)

로드타임

사실은 런타임이죠?

Page 555: 파이썬 생존 안내서 (자막)

로드타임

이때 아무 코드나 실행할 수 있어요.

Page 556: 파이썬 생존 안내서 (자막)

로드타임

이때를 잘 활용하면 생산성을 높이거나최적화하는 데 도움될 수 있습니다.

Page 557: 파이썬 생존 안내서 (자막)

Dog = type('Dog', (object,), {})

effy = Dog()

type 클래스를 인자 3개로 호출하면

Page 558: 파이썬 생존 안내서 (자막)

Dog = type('Dog', (object,), {})

effy = Dog()

런타임에 새로운 클래스를동적으로 만들어낼 수 있어요.

Page 559: 파이썬 생존 안내서 (자막)

for filename in filenames:

with open(filename) as f:

dsl = json.load(f)

cls = generate_class(dsl)

locals()[cls.__name__] = cls

__all__.append(cls.__name__)

이것을 이용하면 파이썬 코드가 아닌다른 파일로 DSL을 만들어서

Page 560: 파이썬 생존 안내서 (자막)

for filename in filenames:

with open(filename) as f:

dsl = json.load(f)

cls = generate_class(dsl)

locals()[cls.__name__] = cls

__all__.append(cls.__name__)

반복적인 클래스 선언 코드를줄일 수 있죠.

Page 561: 파이썬 생존 안내서 (자막)

for filename in filenames:

with open(filename) as f:

dsl = json.load(f)

cls = generate_class(dsl)

locals()[cls.__name__] = cls

__all__.append(cls.__name__)

저희 패킷이나 예외 같은 것도 이렇게로드타임에 동적으로 생성하고 있어요.

Page 562: 파이썬 생존 안내서 (자막)

class Person(Model):

__metaclass__ = ModelMeta

name = field(str)

age = field(int)

>>> Person.schema

[('name', str), ('age', int)]

메타클래스는 클래스의 클래스인데

Page 563: 파이썬 생존 안내서 (자막)

class Person(Model):

__metaclass__ = ModelMeta

name = field(str)

age = field(int)

>>> Person.schema

[('name', str), ('age', int)]

객체가 만들어질 때클래스에 있는 생성자를 부르듯이

Page 564: 파이썬 생존 안내서 (자막)

class Person(Model):

__metaclass__ = ModelMeta

name = field(str)

age = field(int)

>>> Person.schema

[('name', str), ('age', int)]

클래스가 만들어질 땐메타클래스에 정의한 생성자가 불려요.

Page 565: 파이썬 생존 안내서 (자막)

class Person(Model):

__metaclass__ = ModelMeta

name = field(str)

age = field(int)

>>> Person.schema

[('name', str), ('age', int)]

미래에 쓰기 위해 미리 구워 놓을 만한정보가 있다면 여기서 구워 둘 수 있죠.

Page 566: 파이썬 생존 안내서 (자막)

@on_msg(Ping)

def ping_received(msg):

return Pong(msg.seq)

while True:

dispatch_msg(server.recv_msg())

데코레이터도 보통로드타임에 평가되니까

Page 567: 파이썬 생존 안내서 (자막)

@on_msg(Ping)

def ping_received(msg):

return Pong(msg.seq)

while True:

dispatch_msg(server.recv_msg())

마찬가지로 뭔가구워 두는 데 쓸 수 있겠죠?

Page 568: 파이썬 생존 안내서 (자막)

@on_msg(Ping)

def ping_received(msg):

return Pong(msg.seq)

while True:

dispatch_msg(server.recv_msg())

저희 서버에선 패킷 핸들러를이런 식으로 선언하는데

Page 569: 파이썬 생존 안내서 (자막)

@on_msg(Ping)

def ping_received(msg):

return Pong(msg.seq)

while True:

dispatch_msg(server.recv_msg())

런타임에 패킷 디스패칭을빠르게 하기 위해서

Page 570: 파이썬 생존 안내서 (자막)

@on_msg(Ping)

def ping_received(msg):

return Pong(msg.seq)

while True:

dispatch_msg(server.recv_msg())

로드 타임에 라우팅 테이블이미리 구워지도록 하고 있어요.

Page 571: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

로드타임은 처음 진입하는 런타임일 뿐이고파이썬은 엄청나게 동적인 언어다 보니

Page 572: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

이런 온갖 지저분한 일을할 수는 있지만

Page 573: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

너무 과하면 코드를 이해하거나디버깅하기 어려워질 수 있으니까

Page 574: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

중용의 미를 갖추는 게 중요해요.

Page 575: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

적당한 선에서 반복작업을 줄여주거나

Page 576: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

런타임에 처리할 일들을로드타임으로 당겨오는 데 이용하면

Page 577: 파이썬 생존 안내서 (자막)

class Dirty(object):

for x in range(100):

r = random()

locals()['x%f' % r] = r

exec '__metaclass__ = DirtyMeta'

서버처럼 오랫동안 도는 프로그램에선좋은 성과를 보실 수 있을 겁니다.

Page 578: 파이썬 생존 안내서 (자막)

메모리 최적화

Page 579: 파이썬 생존 안내서 (자막)

메모리 최적화

이번엔 저희가 메모리 사용량을 줄이려고 쓰는몇 가지 방법을 소개할게요.

Page 580: 파이썬 생존 안내서 (자막)

object.__slots__

첫 번째로 소개할 건__slots__입니다.

Page 581: 파이썬 생존 안내서 (자막)

Point(1, 2)

{'x': 1,'y': 2,

}

__dict__

파이썬 객체는 내부적으로이렇게 생겼어요.

Page 582: 파이썬 생존 안내서 (자막)

Point(1, 2)

{'x': 1,'y': 2,

}

__dict__

인스턴스마다 속성명을 키로,속성값을 값으로 갖는 dict가 달려있죠.

Page 583: 파이썬 생존 안내서 (자막)

Point(1, 2)

{'x': 1,'y': 2,

}

__dict__

파이썬 객체에 아무 속성이나 동적으로넣었다 뺐다 할 수 있는 이유가 바로 이것이에요.

Page 584: 파이썬 생존 안내서 (자막)

Point(1, 2)

{'x': 1,'y': 2,

}

__dict__

그런데 모든 인스턴스에속성명이 들어간다는 게 좀 아깝잖아요?

Page 585: 파이썬 생존 안내서 (자막)

Point(1, 2)

{'x': 1,'y': 2,

}

__dict__

dict에도 오버헤드가아예 없지는 않을 것 같고요.

Page 586: 파이썬 생존 안내서 (자막)

Point(1, 2)

2x

1

y

__slots__를 쓰면 dict 없이객체가 속성값을 직접 가리키게 할 수 있습니다.

Page 587: 파이썬 생존 안내서 (자막)

class Point(object):

__slots__ = ('x', 'y')

def __init__(self, x, y):

self.x, self.y = x, y

클래스 선언할 때 __slots__에속성명들을 지정해주면 되는데

Page 588: 파이썬 생존 안내서 (자막)

>>> p = Point(1, 2)

>>> p.x = 3

>>> p.z = 3

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Point' object has no attribute 'z'

대신 동적인 특성은 포기해야 해요.

Page 589: 파이썬 생존 안내서 (자막)

>>> p = Point(1, 2)

>>> p.x = 3

>>> p.z = 3

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Point' object has no attribute 'z'

그래서 저희는모든 클래스에 쓰진 못 하고

Page 590: 파이썬 생존 안내서 (자막)

>>> p = Point(1, 2)

>>> p.x = 3

>>> p.z = 3

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Point' object has no attribute 'z'

별로 동적일 필요가 없거나

Page 591: 파이썬 생존 안내서 (자막)

>>> p = Point(1, 2)

>>> p.x = 3

>>> p.z = 3

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Point' object has no attribute 'z'

인스턴스가 아주 많이 생성되는클래스 위주로 적용하고 있습니다.

Page 592: 파이썬 생존 안내서 (자막)

objgraph

다음으로 메모리 누수를 찾아내고방지하는 방법을 소개할게요.

Page 593: 파이썬 생존 안내서 (자막)

objgraph

써드파티 라이브러리인 objgraph는메모리 누수를 찾을 때 굉장히 유용한데

Page 594: 파이썬 생존 안내서 (자막)

>>> objgraph.show_most_common_types()

X 9999

wrapper_descriptor 1085

function 1020

builtin_function_or_method 699

method_descriptor 538

show_most_common_types()를 부르면

Page 595: 파이썬 생존 안내서 (자막)

>>> objgraph.show_most_common_types()

X 9999

wrapper_descriptor 1085

function 1020

builtin_function_or_method 699

method_descriptor 538

메모리에 가장 많이 떠 있는 타입탑10을 알 수 있고

Page 596: 파이썬 생존 안내서 (자막)

>>> xs = objgraph.by_type('X')

>>> len(xs)

9999

by_type()을 부르면타입 이름만으로

Page 597: 파이썬 생존 안내서 (자막)

>>> xs = objgraph.by_type('X')

>>> len(xs)

9999

그 타입에 해당하는 객체를모조리 얻을 수 있어요.

Page 598: 파이썬 생존 안내서 (자막)

objgraph.show_backrefs(xs[42])

show_backrefs()로는 어디서주어진 객체를 참조하는지 알아낼 수 있는데

Page 599: 파이썬 생존 안내서 (자막)

이렇게 그래프 형태로 시각화해줘서

Page 600: 파이썬 생존 안내서 (자막)

이상한 참조를발견하기에 좋습니다.

Page 601: 파이썬 생존 안내서 (자막)

•쓰레기 수거

•참조 횟수 추적

CPython에선 GC와 레퍼런스카운팅이둘 다 쓰이는데

Page 602: 파이썬 생존 안내서 (자막)

•쓰레기 수거

•참조 횟수 추적

레퍼런스카운팅은다소 역사적인 이유로 남아있는 거라

Page 603: 파이썬 생존 안내서 (자막)

•쓰레기 수거

•참조 횟수 추적

PyPy에선 GC만 쓰인다고 합니다.

Page 604: 파이썬 생존 안내서 (자막)

•쓰레기 수거

•참조 횟수 추적

CPython에서 대부분의 객체는레퍼런스카운트가 0이 됐을 때 바로 지워지고

Page 605: 파이썬 생존 안내서 (자막)

•쓰레기 수거

•참조 횟수 추적

오직 쓰레기 순환참조만 GC로 지워지죠.

Page 606: 파이썬 생존 안내서 (자막)

>>> import gc

>>> len(gc.get_objects())

19891212

>>> gc.collect()

19891170

>>> len(gc.get_objects())

42

GC는 표준 라이브러리gc로 접근할 수 있고

Page 607: 파이썬 생존 안내서 (자막)

>>> import gc

>>> len(gc.get_objects())

19891212

>>> gc.collect()

19891170

>>> len(gc.get_objects())

42

collect()를 불러서직접 돌릴 수도 있어요.

Page 608: 파이썬 생존 안내서 (자막)

>>> import gc

>>> len(gc.get_objects())

19891212

>>> gc.collect()

19891170

>>> len(gc.get_objects())

42

사실 불필요한 순환 참조만 방지하면

Page 609: 파이썬 생존 안내서 (자막)

>>> import gc

>>> len(gc.get_objects())

19891212

>>> gc.collect()

19891170

>>> len(gc.get_objects())

42

대부분은 레퍼런스카운팅에 의해서바로바로 지워지게 만들 수 있어요.

Page 610: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

그걸 보조하기 위해 werkref라는표준 라이브러리가 있는데

Page 611: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

단순한 포인터 같은 것뿐만 아니라

Page 612: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

마치 그 객체인양쓸 수 있게 해주는 proxy나

Page 613: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

원소를 약한 참조로관리해주는 WeakSet 같은

Page 614: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

고수준 API도 제공해요.

Page 615: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

물론 이걸 안 써도GC에 의해 언젠가 지워지긴 하겠지만

Page 616: 파이썬 생존 안내서 (자막)

약한 참조

• weakref.ref(referent)

• weakref.proxy(referent)

• weakref.WeakSet

• weakref.WeakKeyDictionary

• weakref.WeakValueDictionary

아무도 GC를 달가워하진 않잖아요?

Page 617: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

한편 아무리 GC가 있다 해도

Page 618: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

영원히 안 죽는 객체가불필요한 참조를 놔주지 않으면

Page 619: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

메모리릭으로 이어지니까

Page 620: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

finally를 이용해 새는 참조가없게 만드는 게 좋아요.

Page 621: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

try 절 끝에 finally 절을붙일 수 있는데

Page 622: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

try 절이 성공적으로 끝나든 오류가 나든finally 절은 무조건 불리게 돼요.

Page 623: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

쓸모 없어진 참조는여기서 바로바로 지워주는 게 좋겠죠.

Page 624: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

try:

self.fight()

finally:

del self.victim

Go에 있는 defer나C++의 RAII를 떠올리심 될 것 같아요.

Page 625: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

self.fight()

del self.victim

finally 없이그냥 함수 끝에서 정리하기만 하면

Page 626: 파이썬 생존 안내서 (자막)

def fight_with_animal(self):

self.victim = load_animal()

self.fight()

del self.victim

Error!

이렇게 중간에 오류 났을 때정리되지 않으니까 꼭 넣어주세요.

Page 627: 파이썬 생존 안내서 (자막)

@contextmanager

def animal_victim(self):

self.victim = load_animal()

try:

yield

finally:

del self.victim

def fight_with_animal(self):

with self.animal_victim():

self.fight()

try-finally를 직접 쓰는 대신with 문을 쓰는 것도 좋은데

Page 628: 파이썬 생존 안내서 (자막)

@contextmanager

def animal_victim(self):

self.victim = load_animal()

try:

yield

finally:

del self.victim

def fight_with_animal(self):

with self.animal_victim():

self.fight()

표준 라이브러리 contextlib에 있는contextmanager를 쓰면

Page 629: 파이썬 생존 안내서 (자막)

@contextmanager

def animal_victim(self):

self.victim = load_animal()

try:

yield

finally:

del self.victim

def fight_with_animal(self):

with self.animal_victim():

self.fight()

with 문 용 객체를쉽게 만들 수 있어요.

Page 630: 파이썬 생존 안내서 (자막)

@contextmanager

def animal_victim(self):

self.victim = load_animal()

try:

yield

finally:

del self.victim

def fight_with_animal(self):

with self.animal_victim():

self.fight()

비슷한 try-finally가여기저기에서 반복적으로 필요하다면

Page 631: 파이썬 생존 안내서 (자막)

@contextmanager

def animal_victim(self):

self.victim = load_animal()

try:

yield

finally:

del self.victim

def fight_with_animal(self):

with self.animal_victim():

self.fight()

이렇게 contextmanager로만들어 두는 게 좋습니다.

Page 632: 파이썬 생존 안내서 (자막)

•with, finally

•weakref

•objgraph

요약하자면 저희는

Page 633: 파이썬 생존 안내서 (자막)

•with, finally

•weakref

•objgraph

with와 finally로참조가 새지 않게 정리해주고

Page 634: 파이썬 생존 안내서 (자막)

•with, finally

•weakref

•objgraph

약한 참조로 순환참조를 막고

Page 635: 파이썬 생존 안내서 (자막)

•with, finally

•weakref

•objgraph

그럼에도 메모리릭이 발견되면objgraph로 디버깅 하고 있습니다.

Page 636: 파이썬 생존 안내서 (자막)

•with, finally

•weakref

•objgraph

그런데 서버를 만드는 게 아니라면이렇게까지 신경쓰진 않으셔도 될 것 같아요.

Page 637: 파이썬 생존 안내서 (자막)

흔한 실수

Page 638: 파이썬 생존 안내서 (자막)

흔한 실수

거의 끝나가는데요

Page 639: 파이썬 생존 안내서 (자막)

흔한 실수

저희 프로젝트에서많은 분들과 협업하면서 발견한

Page 640: 파이썬 생존 안내서 (자막)

흔한 실수

흔한 실수 몇 가지를 모아봤어요.

Page 641: 파이썬 생존 안내서 (자막)

f = open('logs')

logs = f.read()

with open('logs') as f:

logs = f.read()

우선 with 문 없이 파일을 열고나서안 닫는 경우예요.

Page 642: 파이썬 생존 안내서 (자막)

f = open('logs')

logs = f.read()

with open('logs') as f:

logs = f.read()

스트리밍 같은 이유로 파일을 열어놓고있어야 하는 특별한 경우가 아니면

Page 643: 파이썬 생존 안내서 (자막)

f = open('logs')

logs = f.read()

with open('logs') as f:

logs = f.read()

꼭 with 문을 써서 필요 없게 된파일이 바로 닫힐 수 있게 해주세요.

Page 644: 파이썬 생존 안내서 (자막)

def make_node(children=[]):

return Node(children)

이건 인자 기본값으로가변 객체를 지정해둔 모습인데

Page 645: 파이썬 생존 안내서 (자막)

def make_node(children=[]):

return Node(children)

함수 기본값은 로드타임에딱 한 번만 초기화되니까

Page 646: 파이썬 생존 안내서 (자막)

def make_node(children=None):

if children is None:

children = []

return Node(children)

꼭 불변 객체를 써주세요.

Page 647: 파이썬 생존 안내서 (자막)

def make_node(children=None):

if children is None:

children = []

return Node(children)

플레이스홀더로None을 쓰는 걸 추천합니다.

Page 648: 파이썬 생존 안내서 (자막)

>>> n1 = make_node()

>>> n1.children.append(n1)

>>> n2 = make_node()

>>> n2.children

[<n1>]

그러지 않을 경우

Page 649: 파이썬 생존 안내서 (자막)

>>> n1 = make_node()

>>> n1.children.append(n1)

>>> n2 = make_node()

>>> n2.children

[<n1>]

이렇게 엉뚱한 곳에서간섭을 발견하게 될 수도 있어요.

Page 650: 파이썬 생존 안내서 (자막)

try:

do_something()

except Exception as exc:

handle_error(exc)

이번엔 모든 예외를 잡기 위해Exception 클래스를 적은 모습인데

Page 651: 파이썬 생존 안내서 (자막)

try:

do_something()

except Exception as exc:

handle_error(exc)

최상위 예외처럼 생겼죠?

Page 652: 파이썬 생존 안내서 (자막)

• BaseException

• SystemExit

• KeyboardInterrupt

• GeneratorExit

• Exception

• StopIteration

• StandardError

• ValueError, KeyError, IndexError, ...

• Warning

• DeprecationWarning, FutureWarning, ...

하지만 파이썬 표준 예외 체계는이렇게 돼있어서

Page 653: 파이썬 생존 안내서 (자막)

• BaseException

• SystemExit

• KeyboardInterrupt

• GeneratorExit

• Exception

• StopIteration

• StandardError

• ValueError, KeyError, IndexError, ...

• Warning

• DeprecationWarning, FutureWarning, ...

Exception으로 잡으면이쪽은 잡을 수 있지만

Page 654: 파이썬 생존 안내서 (자막)

• BaseException

• SystemExit

• KeyboardInterrupt

• GeneratorExit

• Exception

• StopIteration

• StandardError

• ValueError, KeyError, IndexError, ...

• Warning

• DeprecationWarning, FutureWarning, ...

이쪽이 안 잡혀요.

Page 655: 파이썬 생존 안내서 (자막)

• BaseException

• SystemExit

• KeyboardInterrupt

• GeneratorExit

• Exception

• StopIteration

• StandardError

• ValueError, KeyError, IndexError, ...

• Warning

• DeprecationWarning, FutureWarning, ...

모든 예외를 빠짐 없이 잡고 싶으면

Page 656: 파이썬 생존 안내서 (자막)

• BaseException

• SystemExit

• KeyboardInterrupt

• GeneratorExit

• Exception

• StopIteration

• StandardError

• ValueError, KeyError, IndexError, ...

• Warning

• DeprecationWarning, FutureWarning, ...

진짜 최상위 예외인BaseException으로 잡아주세요.

Page 657: 파이썬 생존 안내서 (자막)

self.found = False

while not self.found:

gevent.sleep(0.001)

self.found = True

마지막으로 스핀락 코드예요.

Page 658: 파이썬 생존 안내서 (자막)

self.found = False

while not self.found:

gevent.sleep(0.001)

self.found = True

이렇게 하면 스레드가 쓸데 없이자꾸 깨어나서

Page 659: 파이썬 생존 안내서 (자막)

self.found = False

while not self.found:

gevent.sleep(0.001)

self.found = True

동시성이 떨어질 수 있어요.

Page 660: 파이썬 생존 안내서 (자막)

self.found = False

while not self.found:

gevent.sleep(0.001)

self.found = True

스레드 10,000개가이러고 있다고 생각하면 끔찍하죠?

Page 661: 파이썬 생존 안내서 (자막)

self.found = Event()

self.found.wait()self.found.set()

뭔가 기다릴 때는 꼭

Page 662: 파이썬 생존 안내서 (자막)

self.found = Event()

self.found.wait()self.found.set()

Event나 Semaphore같은동기화 프리미티브를 잘 써서

Page 663: 파이썬 생존 안내서 (자막)

self.found = Event()

self.found.wait()self.found.set()

깨어날 시간을 스케줄러에게 맡겨야 해요.

Page 664: 파이썬 생존 안내서 (자막)

self.found = Event()

self.found.wait()self.found.set()

이런 동기화 프리미티브는gevent 뿐만 아니라

Page 665: 파이썬 생존 안내서 (자막)

self.found = Event()

self.found.wait()self.found.set()

표준 멀티스레딩을 비롯한모든 비동기 I/O 라이브러리에서

Page 666: 파이썬 생존 안내서 (자막)

self.found = Event()

self.found.wait()self.found.set()

제공되고 있습니다.꼭 찾아서 써주세요.

Page 667: 파이썬 생존 안내서 (자막)

•with open()

•def f(default=None)

•except BaseException

•Event.wait()

네 사례 모두 사소하지만

Page 668: 파이썬 생존 안내서 (자막)

•with open()

•def f(default=None)

•except BaseException

•Event.wait()

성능을 떨어뜨리거나버그를 야기할 수 있고

Page 669: 파이썬 생존 안내서 (자막)

•with open()

•def f(default=None)

•except BaseException

•Event.wait()

파이썬에 익숙하지 않으면쉽게 겪을 수 있을 것 같아요.

Page 670: 파이썬 생존 안내서 (자막)

•with open()

•def f(default=None)

•except BaseException

•Event.wait()

파이썬으로 코딩하실 때이런 것에 주의해주시면 좋겠습니다.

Page 671: 파이썬 생존 안내서 (자막)

오픈소스

Page 672: 파이썬 생존 안내서 (자막)

오픈소스

마지막 주제는 오픈소스예요.

Page 673: 파이썬 생존 안내서 (자막)

1. 자바스크립트 324k

2. 자바 223k

3. 파이썬 165k

7. C++ 87k

8. C 73k

10. C# 56k

출처: http://githut.info/

파이썬의 가장 큰 강점은성숙한 오픈소스 커뮤니티라고 생각합니다.

Page 674: 파이썬 생존 안내서 (자막)

1. 자바스크립트 324k

2. 자바 223k

3. 파이썬 165k

7. C++ 87k

8. C 73k

10. C# 56k

출처: http://githut.info/

GitHut이라는 GitHub 통계사이트를 보면2014년 4쿼터 기준으로

Page 675: 파이썬 생존 안내서 (자막)

1. 자바스크립트 324k

2. 자바 223k

3. 파이썬 165k

7. C++ 87k

8. C 73k

10. C# 56k

출처: http://githut.info/

파이썬의 오픈소스 커뮤니티 규모는3위를 차지했다고 해요.

Page 676: 파이썬 생존 안내서 (자막)

PyPIPython Package Index

파이썬에는 PyPI라는중앙 패키지 저장소가 있는데

Page 677: 파이썬 생존 안내서 (자막)

PyPIPython Package Index

저는 좋은 써드파티 라이브러리를 발굴하러자주 들어가고 있어요.

Page 678: 파이썬 생존 안내서 (자막)

https://pypi.python.org/pypi

profiling

PyPI에선 이렇게공개돼있는 모든 패키지를 검색할 수 있고

Page 679: 파이썬 생존 안내서 (자막)

❯ profiling --version

command not found: profiling

❯ pip install profiling

❯ profiling --version

profiling, version 0.1.1

그렇게 찾은 패키지는pip install 명령어로 설치할 수 있어요.

Page 680: 파이썬 생존 안내서 (자막)

virtualenv

이때 보통 프로젝트 간에의존성이 섞이지 않게 하기 위해서

Page 681: 파이썬 생존 안내서 (자막)

virtualenv

virtualenv라는 걸 씁니다.

Page 682: 파이썬 생존 안내서 (자막)

virtualenv

Node.js의 npm이 시스템이 아닌 현재 폴더에의존성 푸는 걸 생각하심 될 것 같아요.

Page 683: 파이썬 생존 안내서 (자막)

❯ pip install virtualenv

❯ virtualenv ~/env

❯ which python

/usr/bin/python

❯ source ~/env/bin/activate

(env)❯ which python

~/env/bin/python

virtualenv로 적당한 폴더에격리된 환경을 만들고

Page 684: 파이썬 생존 안내서 (자막)

❯ pip install virtualenv

❯ virtualenv ~/env

❯ which python

/usr/bin/python

❯ source ~/env/bin/activate

(env)❯ which python

~/env/bin/python

그 환경을 활성화하면

Page 685: 파이썬 생존 안내서 (자막)

❯ pip install virtualenv

❯ virtualenv ~/env

❯ which python

/usr/bin/python

❯ source ~/env/bin/activate

(env)❯ which python

~/env/bin/python

시스템에 깔린 파이썬 대신

Page 686: 파이썬 생존 안내서 (자막)

❯ pip install virtualenv

❯ virtualenv ~/env

❯ which python

/usr/bin/python

❯ source ~/env/bin/activate

(env)❯ which python

~/env/bin/python

격리된 파이썬이 쓰이게 되죠.

Page 687: 파이썬 생존 안내서 (자막)

(env)❯ pip install profiling

(env)❯ python -c 'import profiling'

(env)❯ deactivate

❯ python -c 'import profiling'

ImportError: No module named profiling

이 안에서 설치하는 라이브러리는안에만 깔리고

Page 688: 파이썬 생존 안내서 (자막)

(env)❯ pip install profiling

(env)❯ python -c 'import profiling'

(env)❯ deactivate

❯ python -c 'import profiling'

ImportError: No module named profiling

밖에는 영향을 주지 않아요.

Page 689: 파이썬 생존 안내서 (자막)

의존성

•setup.py

• requirements.txt

한편 프로젝트가써드파티 라이브러리에 의존성을 가지게 하려면

Page 690: 파이썬 생존 안내서 (자막)

의존성

•setup.py

• requirements.txt

setup.py를 만들거나requirements.txt를 만들면 돼요.

Page 691: 파이썬 생존 안내서 (자막)

from setuptools import setup

setup(

name='foobar',

install_requires=[

'profiling',

'trueskill',

'tossi==0.0.1',

],

)

setup.py

setup.py는 프로젝트 메타정보를정의하는 스크립트인데

Page 692: 파이썬 생존 안내서 (자막)

from setuptools import setup

setup(

name='foobar',

install_requires=[

'profiling',

'trueskill',

'tossi==0.0.1',

],

)

setup.py

install_requires 옵션에의존성을 적어두면

Page 693: 파이썬 생존 안내서 (자막)

from setuptools import setup

setup(

name='foobar',

install_requires=[

'profiling',

'trueskill',

'tossi==0.0.1',

],

)

setup.py

이 프로젝트를 설치할 때의존성까지 자동으로 풀리게 돼요.

Page 694: 파이썬 생존 안내서 (자막)

from setuptools import setup

setup(

name='foobar',

install_requires=[

'profiling',

'trueskill',

'tossi==0.0.1',

],

)

setup.py

여기에 패키지명만 적거나버전을 함께 명시하면 됩니다.

Page 695: 파이썬 생존 안내서 (자막)

profiling

trueskill

tossi==0.0.1

-r lib/baz/requirements.txt

-e git+https://github.com/sublee/hangulize

requirements.txt

requirements.txt는 그냥

Page 696: 파이썬 생존 안내서 (자막)

profiling

trueskill

tossi==0.0.1

-r lib/baz/requirements.txt

-e git+https://github.com/sublee/hangulize

requirements.txt

pip install 명령어에 넘길인자를 적어 놓은 텍스트 파일이에요.

Page 697: 파이썬 생존 안내서 (자막)

❯ pip install -r requirements.txt

requirements.txt

이렇게 쓰죠.

Page 698: 파이썬 생존 안내서 (자막)

❯ pip install -r requirements.txt

requirements.txt

아주 공식적인 방법은 아니지만

Page 699: 파이썬 생존 안내서 (자막)

profiling

trueskill

tossi==0.0.1

-r lib/baz/requirements.txt

-e git+https://github.com/sublee/hangulize

requirements.txt

setup.py로는 만들 수 없는 좀 더 복잡한의존성까지 걸 수 있어서인지

Page 700: 파이썬 생존 안내서 (자막)

profiling

trueskill

tossi==0.0.1

-r lib/baz/requirements.txt

-e git+https://github.com/sublee/hangulize

requirements.txt

여러 곳에서 통용되고 있어요.

Page 701: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

의존성 걸 때패키지명만 적어도 괜찮긴 하지만

Page 702: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

마냥 최신 버전을 받아들이면그 라이브러리가 하위호환을 포기했을 때

Page 703: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

우리 프로젝트 빌드도같이 깨질 우려가 있어요.

Page 704: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

가끔 그런 사고가 생기더라고요.

Page 705: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

대부분의 오픈소스 패키지는시맨틱버저닝이라는 규칙을 따르는데

Page 706: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

이 규칙에서 세 번째 버전값을 올릴 땐어지간해선

Page 707: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

기능이 크게 바뀌거나하위호환이 깨지는 일은 없으니까

Page 708: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

의존성을 특정 버전에 아예 고정해 놓거나

Page 709: 파이썬 생존 안내서 (자막)

tossi==0.0.1

tossi>=0.0.1,<0.1

아니면 두 번째 버전값만이라도고정해두는 게 좋습니다.

Page 710: 파이썬 생존 안내서 (자막)

Profiling, SmartFormat for Python, Tossi, astdispatch, valuedispatch, Gauge

저희는 그동안 파이썬 커뮤니티를 비롯해오픈소스 문화의 도움을 정말 많이 받아왔고

Page 711: 파이썬 생존 안내서 (자막)

Profiling, SmartFormat for Python, Tossi, astdispatch, valuedispatch, Gauge

저희도 그 문화에 동참하고자꾸준히 오픈소스 활동을 해오고 있어요.

Page 712: 파이썬 생존 안내서 (자막)

Profiling, SmartFormat for Python, Tossi, astdispatch, valuedispatch, Gauge

이 프로젝트들은 저희가 개설해서GitHub에 공개해둔 것이고

Page 713: 파이썬 생존 안내서 (자막)

Profiling, SmartFormat for Python, Tossi, astdispatch, valuedispatch, Gauge

2.4k

그중 Profiling은 관심을 좀 받아서2천 4백여 개의 별을 모을 수 있었습니다.

Page 714: 파이썬 생존 안내서 (자막)

gevent, Couchbase Python Client, Base-65536 for Python, Babel

그밖에 가져다 쓰던 도구에서 발견한 문제를고쳐서 패치한 적도 종종 있었고

Page 715: 파이썬 생존 안내서 (자막)

Couchbase, GitLab, MsgPack for CLI

직접 고칠 여력이나 능력이 없어서

Page 716: 파이썬 생존 안내서 (자막)

Couchbase, GitLab, MsgPack for CLI

이슈를 던지고 논의에 참여하는 식으로기여한 적도 종종 있었어요.

Page 717: 파이썬 생존 안내서 (자막)

파이썬은 오픈소스 커뮤니티가활발한 언어인 만큼

Page 718: 파이썬 생존 안내서 (자막)

오픈소스 문화를 가까이 하지 않고서는

Page 719: 파이썬 생존 안내서 (자막)

그 잠재력을 최대한으로 발휘하기어렵다고 생각합니다.

Page 720: 파이썬 생존 안내서 (자막)

파이썬을 계기로, 아니면 이 발표를 계기로

Page 721: 파이썬 생존 안내서 (자막)

오픈소스 문화가 우리 회사에도조금씩 스며들 수 있으면 좋겠어요.

Page 722: 파이썬 생존 안내서 (자막)
Page 723: 파이썬 생존 안내서 (자막)

파이썬은

Page 724: 파이썬 생존 안내서 (자막)

표준 라이브러리가 빵빵한 걸배터리를 내장했다고 표현하는데

Page 725: 파이썬 생존 안내서 (자막)

그런 배터리와

Page 726: 파이썬 생존 안내서 (자막)

커뮤니티가 빵빵한 언어예요.

Page 727: 파이썬 생존 안내서 (자막)

이런 특성을 잘 이해하면최대한 코딩을 적게 하면서도

Page 728: 파이썬 생존 안내서 (자막)

파이썬으로 재밌게작업하실 수 있을 겁니다.

Page 729: 파이썬 생존 안내서 (자막)

아울러 오늘 이야기한 내용은저 혼자가 아니라

Page 730: 파이썬 생존 안내서 (자막)

저희 스튜디오 분들과함께 쌓은 노하우라는 걸 알려드려요.

Page 731: 파이썬 생존 안내서 (자막)

그리고 분명 저희보다 더 좋은 방법을쓰고 계신 분들도 많을 거라고 생각하는데

Page 732: 파이썬 생존 안내서 (자막)

그런 여러분의 노하우도공유 받을 수 있으면 좋겠습니다.

Page 733: 파이썬 생존 안내서 (자막)

감사합니다.

들어주셔서 감사합니다.이것으로 발표를 마칩니다.