본문 바로가기

Study

[파이썬 알고리즘 인터뷰] 1부. 코딩 인터뷰 및 2부. 파이썬(中 3장 파이썬)

본 글은 저자 박상길 님의 '파이썬 알고리즘 인터뷰'를 읽은 후 작성한 것임을 사전에 밝힙니다.
전체 글을 모두 요약하기보다는 데이터분석가로서 필요한 내용 위주로 기록한 내용입니다.

[1부. 코딩인터뷰]

-온라인 코딩 테스트의 사전 준비사항 중...

 : 자주 쓰이는 동작들에 대해서는 코드 스니펫(Code snippet)을 미리 만들어 두면 도움이 된다.

  코드 스니펫이란, 자주 쓰는 동작/작업에 관련된 코드를 정리해두고 빠르게 가져올 수 있도록 정리한 것이다. 

  깃허브 기스트(Github Gist) 와 같이 코드 스니펫을 관리할 수 있는 좋은 무료 온라인 서비스도 있다.

 

[2부. 파이썬 ]

 

20210131_파이썬알고리즘인터뷰_2부_파이썬_3장_파이썬

3장. 파이썬

  • Python version 3.7 기준

-파이썬 문법

Indent

  • 파이썬의 대표적인 특징인 인덴트(Indent)는 공식 가이드인 PEP8에 따라 공백 4칸을 원칙으로 함
  • 과거에는 인덴트를 탭(Tab)으로 하거나 2칸을 권할때도 많았지만, PEP8에 따라 이 기준을 준수 함
In [1]:
# 첫번째 부분에 파라미터가 있다면, 파라미터 시작부분에 맞춤
def make_apple(var_one, var_two,
               var_three, var_four):
    print(var_one)
In [2]:
# 첫번째 줄에 파라미터가 없다면, 공백 4칸 인덴트를 한번 더 추가하여 다른행과 구분
def make_apple(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

Naming Convention

  • 자바와 달리 각 단어를 밑줄(_)로 구분하여 표기하는 스네이크 케이스(Snake Case)를 따름
  • 소문자 변수명과 함수명은 기본
  • 파이썬에 대한 굉장한 자부심이 있어서 카멜 케이스(Camel case: 단어별로 대소문자를 구분하여 표기, 단 첫단어는 소문자)를 포함 자바스타일의 코딩을 지양함
In [3]:
# 자바의 Camel case
camelCase: int =1
In [4]:
# 파이썬의 Snake case
snake_case: int =1

Type Hint

  • 파이썬은 동적 타이핑 언어임에도 python version 3.5부터 타입을 지정할 수 있게 되었음
In [5]:
# int형 a를 받아서 bool타입을 리턴하는 함수라는것을 표시할 뿐(가독성 측면) 여전히 동적할당이긴 함
def make_apple(a: int) -> bool:
    print(type(a))

make_apple("1")
<class 'str'>
In [6]:
# 타입힌트에 오류가 있는지 확인하는 방법 : mypy 사용
# $ pip install mypy
# $ mypy 파일명.py

List Comprehension

  • 기존 list를 기반으로 새로운 list를 만들어 내는 구문
  • lambda 표현식 + map, filter 로 사용하는 것 보다 가독성이 좋음
In [7]:
# lambda 표현식 + map 사용해서 새로운 list 만들기
list(map(lambda x: x+10, [1,2,3]))
Out[7]:
[11, 12, 13]
In [8]:
# List Comprehension으로 작성하기
[x+10 for x in [1,2,3]]
Out[8]:
[11, 12, 13]
In [9]:
# Dictionary Comprehension도 가능 
import pandas as pd
a=pd.DataFrame([[1,2],[3,4]], columns=[1,2])
{key: value for key, value in zip(a[1],a[2])}
Out[9]:
{1: 2, 3: 4}

Generator

  • loop의 iteration(루프의 반복문)을 제어할 수 있는 루틴의 형태
  • 메모리 어딘가에 숫자를 보관한 필요 없이 yield 구문을 통해 제너레이터만 생성해두면 필요할 때 next() 연산을 통해 언제든 숫자 생성 가능
In [10]:
# 예를들어 yield 구문을 사용하여 랜덤값을 뱉어주는 generator 생성
import random

def gen():
    yield random.random()

gen()
Out[10]:
<generator object gen at 0x7fc87b3d57b0>
In [11]:
# 필요할때마다 next()로 추출
g=gen()
next(g)
Out[11]:
0.8350638071107949

Enumerate

  • 순서가 있는 자료형(list, set, tuple)을 인덱스를 포함한 enumerate 객체로 리턴함
In [12]:
# list 원소에 순서를 부여하기
a=['사과', '배', '귤']
list(enumerate(a))
Out[12]:
[(0, '사과'), (1, '배'), (2, '귤')]
In [13]:
# for 문에서 많이 사용하는 enumerate
for i, v in enumerate(a):
    print(i, v)
0 사과
1 배
2 귤

/ 와 // 나눗셈 연산자

  • PEP 238에서 아래와 같은 동작 방식의 변경이 제안 됨
    • Python 2 이하: 5/3 = 1 반환 (정수형 타입 유지)
    • Python 3 이상: 5/3 = 1.666...반환 (정수형 -> 실수형으로 타입 자동 변환)
  • 대신 // 를 사용하면 기존대로 타입을 유지하게 됨(내림의 연산자 역할=몫을 구하는 연산자)
In [14]:
# / 사용
5/3
Out[14]:
1.6666666666666667
In [15]:
# // 사용
5//3
Out[15]:
1
In [16]:
# // 연산자는 int(/) 연산자와 결과적으로 동일하다
int(5/3)
Out[16]:
1

Print

In [17]:
# 가장 쉽게 값을 출력하는 방법은 콤마(,) 사용 -> 띄어쓰기로 값 구분
print('A', 'B')
A B
In [18]:
# sep= 옵션을 이용하면 띄어쓰기가 아닌 다른 구분자를 사용할 수 있음
print('A','B', sep='와 ')
A와 B
In [19]:
# print 함수는 자동적으로 함수 끝에는 줄바꿈이 됨 
print('A')
print('B')
A
B
In [20]:
# end= 옵션을 이용하면 줄바꿈이 아닌 다른 항목(아래 예시에서는 띄어쓰기로)으로 대체 가능
print('A', end=' ')
print('B')
A B
In [21]:
# 리스트 출력시 join()으로 구분자(아래 예시에서는 '와 '로)를 이용하여 묶어서 처리 
a=['A', 'B', 'C']
print('와 '.join(a))
A와 B와 C
In [22]:
# f-string(formated string literal)을 이용하여 직관적이고 빠르게 변수를 출력 -> Python 3.6 이상만 지원
idx=1
fruit='Apple'
print(f'{idx+1}: {fruit}')
2: Apple

구글의 파이썬 스타일 가이드

  • 특히 가독성을 높이기 위한 지침이 많음
In [23]:
# 함수의 기본값으로 가변 객체(Mutable Object: [],{} 등등) 사용하지 말것 -> 함수가 객체를 수정하면 기본값이 변경되기 때문
def foo(c=[]):
    a=c
    a.append('딸기')
    return a

c=['사과']
a=foo(c)

print(a)
print(c)
['사과', '딸기']
['사과', '딸기']
In [24]:
# 대신 불변 객체(Immutable Object)를 사용하거나 
def foo(c=int):
    a=c
    a+=1
    return a

c=1
a=foo(c)

print(a)
print(c)
2
1
In [25]:
# None을 명시적으로 할당
def foo2(c=None):
    if c is None:
        c=['딸기']
        return c
foo2()
Out[25]:
['딸기']

파이썬 가이드 철학

  • 언어 차원에서 Zen of Python이라는 이름으로 철학을 제공
In [26]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

[참고] Python's PEP 8, Google's python style guide