우리 글에서는 알고리즘이란 하나의 큰 줄기 안에서만 파이썬을 다루었지만, 파이썬을 활용해서 프로그래밍을 하는데 또는 파이썬 관련 글들을 검색해서 읽고 이해하는데에 필요한 몇가지 잡다한(?) 지식들이 있다. 이 글에다 앞에서 미처 얘기하지 못했던 여러가지 파편같은 얘기들을 정리해 보고자 한다. 생각나는 대로 추가하는 형태로 정리하겠다. 언제까지 (*작성중*)이 될지는 예상하지 못하겠다.
이야기해 볼 주제는 다음과 같다. 어떤 큰 그림을 그리고 찾아낸 주제가 아니고, 파이썬 공부를 시작하면서 찾아보게 되는 키워드들이다. 그래서, 눈에 띄는대로 계속 업데이트해 볼 생각이다. 물론, 이 중에서 예외(Exception) 와 쓰레드(Thread) 같은 덩치 큰 주제들은 따로 빼서 블로그를 준비해 보겠다. 여기서는 그냥 기본적인 개념만 잡아보는 수준으로 얕게 살펴보도록 하자.
○ .py 와 .pyc
.py 는 파이썬 프로그램 파일(스크립트)의 확장자 이름이다. 그런데 간혹 보다보면 .pyc 라는 확장자를 보게 되는데 이는 compiled python script 라고 보면 되겠다. 파이썬이나 가상환경이 설치된 폴더를 보면 스크립트가 있는 모든 폴더에 __pycache__라는 폴더가 있는데, 이 폴더를 열어보면 확장자가 pyc 인 파일들만 있는 것을 확인할 수 있다.
파이썬은 인터프리터(interpreter) 이면서 컴파일러(compiler)의 특성을 같이 가지고 있는 언어이다. 스크립트를 매번 기계어(machine language, 이진수 코드) 로 번역하여 실행하면 속도가 매우 느리게 된다. 스크립트가 수정되지 않았다면 이전에 만들어 둔 바이너리 코드(binary code)를 실행하는 것이 훨씬 효과적일 것이다.
○ None
None 은 아무값도 아닌 값을 나타내는 예약어(reserved word)이다. 예약어란 정해진 의미 외에는 다른 의미로는 사용될 수 없는 이름(식별자, identifier)을 말한다. 첫글자는 대문자 N 이다. 모든 컴퓨터 언어가 그러하듯, 파이썬도 대소문자를 구별한다. 어떤 함수에서 아무 값도 리턴하지 않으면 None 이 리턴된다. 예로, 인자로 넘어 온 리스트에서 첫번째 짝수값을 찾아서 리턴해 주는 함수 find( d )를 생각해 보자.
이 함수를 실제 호출해 보면, 아래의 In[25]에서 처럼 우리가 찾고자 하는 값이 있는 경우에는 그 값을 찾아서 리턴해 준다. 하지만, In[26]에서와 같이 찾고자 하는 값이 없는 경우에는 None 값이 리턴되고 있음을 확인할 수 있다.
어떤 함수에서 찾고자 하는 값을 찾지 못한 경우에는 None 이라는 값을 명시적으로 리턴하게 만드는 것은 매우 좋은 접근이다. 아래의 예를 보자. 아래의 예는 어떤 리스트에서 첫번째로 발견되는 짝수값을 찾아서 되돌려주는 함수이다. None 도 하나의 값이다. 실제로, r=None 형태로 assignment 할 수 있으며, 그 의미는 "a 변수가 지금은 아무 값도 가리키고 있지 않다" 정도로 해석하면 좋겠다. 작동은 위의 코드와 동일하다.
여기서 한가지 조심할 것은 리턴된 값을 다른 연산에 사용하는 경우이다. 예를 들어, 다음과 같이 None으로 리턴된 값을 연산에 사용하게 되면 당연히 에러가 발생한다.
이런 에러는 프로그램의 실행을 중단하게 만든다. 만약 중간에 종료되어서는 안되는 프로그램이라면 (만약에 로케트의 비행을 통제하는 프로그램인데, 비행 도중에 멈춰버리면 큰일 나겠다) 예외처리를 해주어야 한다. None은 우리가 원하는 값이 아니었으니 예외 (Exception) 이며, 이러한 예외가 발생하면 우리의 프로그램은 예측불가능 상태가 된다. 그 예외도 처리할 수 있는 루틴을 만들어 주어야 한다. 조금 거창하게 얘기를 했는데, 아래처럼 경우를 나누어 준다는 뜻이다.
○ pass
너무 당연한 얘기지만, 우리가 코딩에서 많이 사용하는 for 나 if 는 그 자체로 명령어가 아니다. 다른 명령어를 반복시켜주거나 실행을 선택할 수 있게 만들어 줄 뿐이다. 따라서 for 나 if 다음에는 실행될 명령어가 1개 이상 반드시 따라와야 한다. 그렇지 않으면 에러가 발생한다.
그런데 프로그램을 만드는 과정에서 아직 무슨 명령어가 들어가야 할지 결정되지 않은 경우가 있다. 즉, 어떤 명령어가 들어가긴 해야 하는데, 아직까지는 결정되지 않는 경우이다. 이러한 경우에 pass 라는 키워드가 매우 유용하게 사용될 수 있다. 다음의 예와 같다.
뭐, 별것 아니다 싶긴 한데, 실제 코딩을 하다 보면 껍데기와 알맹이를 구분해야 한다. 껍데기를 인터페이스(interface)라고 부르고 알맹이를 임플리멘테이션(implementation)이라고 부른다. 우리가 잘 아는 함수를 예를 들면, 인터페이스란 함수의 이름을 말하는 것이고, 임플리멘테이션이란 그 함수의 바디(body)를 말하는 것이다.
실제로 많은 함수로 구성된 프로그램을 개발하다 보면, 인터페이스는 만들어 졌는데 임플리멘테이션은 아직 결정되지 않는 경우가 많다. 이런 경우에 pass 란 키워드가 아주 유용하게 활용될 수 있다. 예를 들어 findTheFirstEvenNumber( ) 라는 함수가 필요하긴 한데, 아직 어떤 로직으로 실행될 지는 결정되지 않았다면 다음과 같이 만들어 둘 수 있다.
○ enumerate 함수
우리가 리스트 데이터를 다루는 방식은 크게 2가지이다. 하나는 리스트를 구성하는 각 값에 직접 접근하는 것이고(보통 for each 반복이라고 부른다), 나머지 하나는 인덱스를 이용해서 접근하는 방식이다 (전통적 방식이다). 앞에서도 많이 다루어 봤지만, 복습삼아 [2, 4, 6, 8, 9, 7, 5, 3, 1] 리스트 데이터에 대해 앞의 2가지 방식으로 접근을 해 보면 아래의 코드와 같다. In[37]이 소위 for each 에 의한 접근이고 In[38]이 인덱스를 이용한 접근이다.
enumerate 함수를 이용하면 이 2가지 방식을 하나의 for 문에서 동시에 사용할 수 있다. enumerate( ) 함수의 설명을 보면 다음과 같다. iterable 객체 (리스트를 떠올리면 된다) 에서 enumerate( )함수를 실행하면 인덱스와 값으로 구성된 튜플을 반복해서 리턴해 준다는 의미이다.
실제로 실행해 보면 다음과 같은 결과를 볼 수 있다.
따라서, 다음과 같은 형태로 인덱스와 데이터 값을 동시에 다룰 수 있게 된다.
이 형태보다는 다음의 형태로 많이 사용된다.
알다시피 튜플에서 괄호는 생략할 수 있다. 데이터들이 콤마( , ) 로 나열되어 있는데 괄호로 안 묶여 있다? 그럼 튜플이다.
이 형태에서 만약 우리가 데이터에만 관심이 있다면 (다시 얘기해서, 인덱스는 don't care 라면) 해당하는 위치에 _ (언더스 스코어)를 적어두면 된다. 반대의 경우라면, 두번째 위치에 언더스코어( _ )를 표시하면 된다. 말이 난 김에, 파이썬에서 언더스코어는 아주 다양한 의미로 사용되고 있다. 이에 대해서는 다음 항목에서 살펴보도록 하자.
개인적으로는 선호하지 않는 패턴이지만, 많이 활용되는 함수여서 한번 살펴 보았다.
** 그 외 자세한 내용은 [파이썬 알고리즘 객체지향 코딩의 기술]을 참고하기 바랍니다 **