책 링크 : https://www.onlybook.co.kr/entry/algorithm-interview
(20.08.21 추가)글을 쓰면서 책을 인용/발췌 시 출판사에 미리 허가를 받아야 하는데, 본 글의 경우 그러한 과정을 거치지 않았습니다. 앞으로 비슷한 글을 올릴 때 미리 출판사에 허가를 받고 올리겠습니다. 죄송합니다.
코테 문제를 이따금씩 풀다가 책 뽐뿌가 와서 최근에 나온 <파이썬 알고리즘 인터뷰>라는 책을 질렀습니다. 약 700페이지가 넘어가는데 초반 150페이지 정도를 "파이썬"에 대해 다루고 넘어갑니다. 저는 ML/DL에 관심이 생겨서 파이썬을 필요한 부분만 독학한지라, 체계적으로 배우지는 않았습니다. 학부 수업에서 배운 자료구조, 알고리즘 지식을 바탕으로 조금이나마 코테 문제도 풀고 하면서 효과적으로 코딩해보려고 노력합니다만 부족한 점이 많다고 느끼고 있습니다.
이 책은 진짜 알고리즘 공부 및 코테 대비용으로 샀는데, 초반부 파이썬에 대해 모르고 넘어갈 수도 있던 부분을 꽤 많이, 자세하게, 파이써닉하고 깔끔하게 짚어줍니다. 개인적으로 보면서 기록해두고 싶은 부분을 따로 모아서 정리합니다.
1. 네이밍 컨벤션(Naming Convention)
파이썬으로 코딩하면서 PEP 8이라는 것을 들어보셨을 겁니다. 저는 그냥 "코딩 권장사항"정도로 이해하고 있었는데, 이 개선 제안서(Python Enhancement Proposals)를 통해 파이썬이 개발되고, 많은 사람들의 의견을 수렴해서 업데이트한다는 사실은 몰랐습니다. if문 뒤에 엔터 한 번 누르면 자동으로 생기는 indent도 PEP 8에 따라 공백 4칸을 원칙으로 합니다. 귀찮을 때는 tab으로 대신했는데, PEP 8 이후에는 이 기준을 준수한다고 합니다.
코딩하면서 변수명을 짓는 데 많은 시간을 들입니다. 귀찮아서 그때그때 기분에 따라 짓고 넘어갈 수도 있지만, 나중에 다시 코드를 볼 일이 있을 때 후회하겠죠. 이러면 변수명을 지을 때 권장하는 규칙이 있습니다. 바로 네이밍 컨벤션입니다.
변수명에 띄어쓰기가 들어갈 수 없기 때문에 my model 대신 my_model과 같이 변수명을 만듭니다. 여기서 _를 사용하는데, 이를 스네이크 케이스라고 합니다. 함수명도 마찬가지입니다. 반대로 자바에서는 카멜 케이스를 사용한다고 하는데, myModel과 같이 _ 대신 대문자를 사용합니다. 파이썬은 PEP 8을 통해서 스네이크 케이스를 권장합니다. 나아가 누군가 "왜 이렇게 변수명을 지었어?"라고 물어볼 때 "PEP 8 및 파이썬 철학에 따라 이를 지향한다"라고 이야기하면 더 좋겠죠.
2. 리스트 컴프리헨션(List Comprehension)
코테 문제 풀 때 많이 사용합니다만, 딕셔너리에 대해서도 이게 되는지 몰랐습니다. 됩니다.
ex) a = {key : value for key, value in original.items()}
이상하게 보고 싶지만 보기 두려운 책 중 하나가 <파이썬 코딩의 기술>(길벗, 2016년)입니다.(유사하게 <전문가를 위한 파이썬>도 보고싶긴 하네요. 계속 python 쓸거라면요) 그 책의 1장은 "파이썬다운 생각(Pythonic Thinking)"인데, 거기에서도 map이나 filter 대신 list comprehension을 사용하자라고 할 정도로 list comprehension은 유용하게 사용됩니다. 너무 줄여쓰면 문제가 되긴 하지만, 가독성도 좋구요. 저는 스스로 파이썬을 날림으로 배웠다고 생각하기 때문에 이러한 것들을 언젠가 배워야 한다고 생각하고 있습니다.
3. 제너레이터(Generator)
꽤 오래 전부터 추가되었던 기능 중 하나로 책에서는 "루프의 반복(iteration) 동작을 제어할 수 있는 루틴 형태"라고 소개합니다. for문에서 1억개의 숫자를 사용해야 하는데, for i in range(1억):이라고 하면 1억개의 숫자를 미리 list에 만들고 하나씩 빼서 i를 사용하지는 않을겁니다. 메모리 소비가 어마무시하겠죠.
def n_generator():
n = 0
while True:
n += 1
yield n
보통 def로 함수를 정의하면 원하는 값을 return하는데, 대신 yield를 사용해서 제너레이터를 return하도록 했습니다. while True를 넣어뒀기 때문에, 이 제너레이터는 n=1부터 계속 1씩 커지는 자연수를 return할 수 있습니다. 다음 값을 생성하고 싶다면 next를 이용해 다음과 같이 사용할 수 있습니다.
>>> n_generator() #generator
<generator object n at 0x000001D8D0534A48>
>>> next(n_generator())
1
>>> g = n_generator()
>>> for _ in range(5):
print(next(g))
1
2
3
4
5
흔히 for문에서 쓰는 range()가 생각나네요. 실제로 range()는 제너레이터의 방식을 활용한다고 합니다. 범위 내 값을 미리 만들어서 어디다가 저장하는 것이 아닌, 값을 생성해야 한다는 조건만 가지고 있는 것이죠. 다음 예시를 봅시다.
>>> a = [x for x in range(5)]
>>> b = range(5)
>>> len(a)
5
>>>len(b)
5
>>> a[4]
4
>>> b[4] #인덱스로 바로 접근 가능
4
>>> sys.getsizeof(a)
128
>>> sys.getsizeof(b) #메모리 차이
48
인덱스로 바로 접근도 가능한데 메모리 점유율에서 큰 차이를 보입니다. 필요한 숫자가 커지면 커질수록 더 크게 체감할 수 있겠죠.
4. enumerate
이따금씩 사용하는 enumerate() 함수입니다. for문에서 인덱스와 값을 동시에 뽑고싶을 때 사용하는데 list(enumerate())가 어떻게 생겼는지를 확인하면 쉽게 이해할 수 있습니다
>>> a = [10, 20, 30, 40, 50]
>>> list(enumerate(a))
[(0, 10), (1, 20), (2, 30), (3, 40), (4, 50)]
>>> for i, v in enumerate(a):
print(i, v)
0 10
1 20
2 30
3 40
4 50
위와 같이 사용할 수 있습니다.
책에서는 이외에도 코딩 테스트를 위해, 그리고 파이써닉한 코딩을 위해 몇 가지를 더 소개합니다. 코드가 잘 돌아가게 하는 것도 중요하지만, 내가 나중에 쉽게 알아보기 위해서, 그리고 다른 사람들도 알아볼 수 있게 하기 위해 권장 규칙이라는 것이 존재한다고 생각합니다. 마지막으로 "팀소트"라는 정렬 알고리즘을 만든 팀 피터스라는 사람이 정의한 "파이썬 철학"을 소개합니다. import this로 실행할 수 있다는데, 처음 알았네요...
>>> 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!
"""