gophercon korea 2015 - python 개발자를 위한 go (이경찬)

46
Python 개발자를 위한 Go Gophercon Korea 2015 이경찬

Upload: kyoungchan-lee

Post on 10-Feb-2017

41.990 views

Category:

Software


4 download

TRANSCRIPT

Python 개발자를 위한 Go

Gophercon Korea 2015

이경찬

발표자 소개• 이경찬 (leekchan.com)

• 주로 Python으로 서버 개발

• Newbie Gopher

• Go로 취미삼아 오픈소스 라이브러리 제작중

• accounting - currency formatting (github.com/leekchan/accounting)

• timeutil - Timedelta, Strftime 구현 (github.com/leekchan/timeutil)

• gtf - 유용한 template function 모음 (github.com/leekchan/gtf)

Python

극강의 생산성!

대략 이정도... ???

하지만...

• 대규모 환경에서는 조금 아쉬운 성능

• dynamic typing

• GIL

• C10K problem 해결을 위해 event loop 를 사용하지만 한계가 있는 성능

Go

Go의 장점• Static typing

• 성능

• compile 단계에서 error 확인 가능

• Goroutine (외계기술)

• event loop 없이도 C10K problem 해결

• 멀티코어 지원

• 게다가 훌륭한 생산성까지!

Control structures

if• [Python]

if x > 0:

• [Go]if x > 0 {}

• initialization statementif ok := check(); ok {

for• [Python]

for value in value_list:

• [Go]

• for i, value := range array { // array, slice, map 순회

• for init; condition; post { // c stylefor i := 0; i < 10; i++ {

• for condition { // while 문 처럼 사용

• for { // 무한 루프

switch

• switch x {case 0: …case 1: …default: … }

Type

Dynamic typing vs Static typing

• [Python] Dynamic typing

• foo = 1foo = ‘bar’foo = 1.3

• [Go] Static typing

• var foo int = 1var foo string = “bar”var foo float64 = 1.3

다양한 선언 방법

• var foo int = 1var foo string = “bar”

• var foo = 1var foo = “bar”

• foo := 1foo := “bar”

• type 생략시 대입하는 변수의 type을 따름

다양한 타입• int, int8, int16, int32, int64

• uint, uint8, uint16, uint32, uint64

• float32, float64

• uintptr (uint와 크기 동일)

• complex64, complex128

• byte (uint8과 크기 동일)

• rune

Function

함수 선언• [Python]

• def sum(x, y): return x + y

• [Go]

• func sum(x, y int) int { return x + y }

• 매개변수, 리턴값 모두 type 명시 필요!

• func sum(x, y int) (int, err) { 와 같이 여러개의 리턴값도 리턴 가능!

이쯤에서 생기는 궁금증...• int 말고 다른 type을 더하고 싶으면 어떻게 해야 할까... ???

• method overloading을 하면 되려나???

• func sum(x, y int) int { return x + y }func sum(x, y float64) float64 { return x + y }

• compile error .. - “sum redeclared in this block”

https://golang.org/doc/faq#overloading

Go는 안타깝게도 method overloading을 지원하지 않습니다...

interface{}

• interface는 method의 집합 (단, 메서드 자체를 구현하지는 않음.)

• method가 정의되지 않은 빈 인터페이스(interface{})는 어떠한 조건도 없기 때문에 모든 type의 값 대입이 가능.

• func sum (x, y interface{})

type switch• switch x.(type) {

case int: …case float64: …

• interface는 내부적으로 실제 값의 type에 관한 정보들을 저장하고 있는 itable 에 대한 pointer를 가지고 있음.

• type switch를 사용하면 Go compiler가 itable 을 체크해서 원하는 type이 맞는지 비교하는 코드를 생성해줌.

type assertion• value := x.(int)

• type을 명시적으로 지정

• 맞지 않는 type으로 assertion을 하면 panic! (runtime error)

• 따라서 type switch와 같이 쓰는 것이 일반적switch x.(type) { case int: value := x.(int) …case float64: value := x.(float64) …

reflect package

• run-time reflection 기능 제공

• reflect 패키지는 앞서 살펴본 type switch & type assertion 에 해당하는 기능들과 여러가지 부가 기능을 가지고 있음.

• reflect 패키지의 기능들은 편리하기는 하지만, type switch & type assertion에 비해 더 무거운 연산들이 동반될 수 있으므로 고성능 어플리케이션 작성시에는 세심하게 사용할 필요가 있음.

array, slice, map

array

• var x [10]int // 길이가 10인 int형 배열 선언

• x := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // 생성과 동시에 초기화

• x := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // 배열 길이가 10으로 자동으로 설정됨

• y := x // 새로운 배열에 대입시 값 전체가 복사됨

slice• Python의 list와 쓰임새가 유사함.

• var x []int // 선언

• x := make([]int, 5, 10) // 길이(length)는 6, 용량(capacity)은 10

• 길이 : index로 접근할 수 있는 범위 / 용량 : 실제 할당된 data array 공간

• x := make([]int, 5) // 용량은 생략 가능

• x := []int{0, 1, 2, 3, 4} // 생성과 동시에 초기화

• x = append(x, 1) // 새로운 값 추가x = append(x, 1, 2, 3) // 여러 값을 동시에 추가도 가능

slice 내부 구현• slice의 내부 구현에는 앞서 살펴본 array가 사용됨

• slice 생성시 지정된 용량만큼의 data array를 생성하고 해당 array에 대한 pointer를 가짐.

• 이 구현을 제대로 이해하는 것이 중요!

ptr*Elem

lenint

capint

[]int[5]int

slice 내부 구현 (2)• x := []int{0, 1, 2, 3, 4} // 길이가 5인 slice 생성

• x = append(x, 1) // 해당 slice에 새로운 값을 추가하면?

• append는 slice 용량을 먼저 확인하고 용량이 넘치면 새로운 data array allocation 후 값을 추가한다. (용량이 남아 있으면 바로 값만 대입.)

• 기존 slice 용량이 5 이므로 새로운 allocation이 발생!

• 다행히 append 할때마다 새로 allocation 하지는 않고 future growth를 고려하여 필요한 용량의 2배를 할당.

• append를 매우 빈번하게 할 예정이라면 용량을 예측해서 미리 지정하면 성능에 도움이 됨.

slice 내부 구현 (3)• array는 새로운 변수에 대입시 값이 모두 복사되던데, slice도 혹시?

• slice는 앞서 살펴본 바와 같이 실제 data array에 대한 pointer 이다. 따라서 대입시 pointer만 복사된다.

• x := []int{0, 1, 2, 3, 4}y := x // y와 x는 같은 data array를 참조. y의 데이터 조작시 x도 변경됨.

ptr*Elem

lenint

capint

[]int[5]int

slice 내부 구현 (4)• slice는 아래와 같이 부분 slice 생성이 가능하다. (slicing)

x = x[2:4]이때는 내부적으로 어떻게 동작할까?

• slice는 앞서 살펴본 바와 같이 실제 데이터 array에 대한 pointer 이다. slicing시에도 데이터 array는 그대로 존재하고 해당 데이터를 가리키는 pointer를 가진 slice가 생성된다.

ptr*Elem

lenint

capint

[]int[5]int

map• Python의 dict와 쓰임새가 유사함.

• hash table 구현

• var x map[string]int // 선언 (key type : string / value type : int)

• x := make(map[string]int) // slice와 마찬가지로 make로 할당 가능

• x := map[string]int{“foo”: 1, “bar”: 2} // 할당과 동시에 초기화

• x[“new”] = 2 // 새로운 key로 값 대입x[“foo”] = 4 // 기존 키의 key의 값 변경

• y := x // slice와 마찬가지로 데이터에 대한 pointer만 복사됨. 따라서 y의 데이터 조작시 x도 변경됨.

map key 존재 여부 체크하기• map에 존재하지 않는 key를 조회하게 되면 각 type의 zero value가

리턴됨. (int => 0, string => “”, bool => false 등)x := map[string]int{“foo”: 1, “bar”: 2} value := x[“hello”] // value = 0

• 또는, 아래와 같은 형태로 key 존재 여부 체크도 가능함.value, ok := x[“hello”] // value = 0 (zero value) , ok = false

• if 문에서는 initialization statement를 key 존재 여부 체크 가능.if value, ok := x[“hello”]; ok {

map 사용시 주의사항• Concurrency

• map은 여러 goroutine에서 접근시 동시성을 보장하지 않음.

• map을 여러 goroutine에서 접근할 때는 sync.RWMutex 사용 필요

• Iteration order

• for range loop로 데이터를 가져올때 순서를 보장하지 않음.

struct

struct• Python의 class와 유사하게 사용 가능

• 선언type Accounting struct { Symbol string Precision int Thousand string Decimal string Format string }

• 할당accounting := Accounting{Symbol: "$", Precision: 2}

struct method• class 가 없는 대신 struct에 method 연결 가능type Accounting struct { Symbol string Precision int Thousand string Decimal string Format string }func (accounting *Accounting) FormatMoney(value interface{}) string { …}

• method 호출accounting := Accounting{Symbol: "$", Precision: 2}accounting.FormatMoney(1234567)

panic / recover

panic

• run-time error

• Python의 Exception과 유사

• Python의 Exception 처럼 직접 발생시키는 것도 가능panic(“unsupported type”)

• panic 발생시 적절하게 handling 하지 못하면 어플리케이션이 중단됨.

recover• defer - 함수 종료 직전에 실행. defer를 여러번 호출하면 LIFO로 실

• First Class Function => 익명 함수 호출 가능

• func handler() { defer func() { if err := recover(); err != nil { // panic handling } }() …}

package

package• 소스코드 첫줄에 package명 명시

package accounting

• Python의 module 과 다르게 파일명은 아무런 역할도 하지 않는다.

• 패키지 내부에 있는 함수, 변수, 상수들은 아래 규칙에 따라 역할이 결정됨.

• 첫 글자가 대문자이면 외부에서 import시 접근 가능

• 첫 글자가 소문자이면 package 내부에서만 접근 가능

• 심지어 struct의 경우에는 struct 이름 첫글자가 대문자여도 필드 이름이 소문자이면 외부에서 접근 못함. 외부에서 접근이 필요한 필드들은 대문자를 사용해야 한다. type Timedelta struct { Days, Seconds, Microseconds, Milliseconds, Minutes, Hours, Weeks time.Duration}

package 공유• github 에 repo 생성시 아래 명령어로 바로 패키지 다운로드

및 설치 가능 (bitbucket 등 다른 저장소도 지원) go get github.com/leekchan/accounting

• 처음에는 좋아 보였으나, PyPI에 비해 단점이 많음.

• 무조건 master branch 최신 커밋 소스를 다운로드

• 기능 개발 및 테스트 후 원하는 시점에 versioning을 하고 릴리즈 할 수가 없음

• 해당 단점을 보완하는 다양한 오픈소스 라이브러가 생겨나고 있는 중

gofmt

gofmt

• formatting을 style guide에 맞춰서 알아서 해주는 gofmt 라는 기능을 bult-in 으로 제공

• 덕분에, 일단 코드를 쓰고 커밋하기 전에 gofmt을 한번 실행시켜 주면 style guide에 맞춰서 아름답게 formatting이 됨.

• Python에서 PEP 8 준수를 위해 많은 linter들이 있지만 Go에서는 이미 built-in 기능인 gofmt가 끝판왕.

감사합니다.