[Python101] Iterable (1) – 리스트

지난 시간 for 문을 설명할 때 다음과 같은 문법이 잠깐 등장했다.

for number in range(1,10):

이 구문은 range() 함수를 사용해 만들어지는 1~9 까지의 숫자의 ‘집합’의 개별 원소에 대해 반복적인 명령을 수행하는 구문이라고 했다. 이러한 집합은 사실 영어로 ‘iterable’이라고 하지만 우리말로는 딱히 정확히 대응시킬만한 말이 없다는 것도 이야기했다. 이번 시간에는 이 iterable에 대해서 알아보고자 한다.

* 이번 시간은 IDLE의 대화형 쉘을 통해 직접 확인해보면서 배우는 것이 좋다.

지난 시간에서 사용한 ‘집합’이라는 표현이 어찌보면 가장 근접한 표현일 수도 있다. iterable이라는 말은 ‘집합’ 그 자체보다는 “개별 원소를 반복적으로 셀 수 있는”이라는 문맥적인 의미가 있기 때문이다. 파이썬에서 사용하는 집합에는 1) 리스트, 2) 튜플, 3) 사전(dictionary) 가 있다. 각각은 약간의 차이점은 있으나, 대체로 for 문과 같은 반복 작업에서 원소들을 일일이 열거 한다는 점에서 공통점을 가진다.

리스트

프로그래밍 언어를 배울 때 빠지지 않고 등장하는 개념이 있으니, 바로 ‘배열(array)’이다. 배열은 수학에서의 ‘집합’과 매우 유사하기도 한데, 파이썬의 리스트는 바로 이 배열과 거의 같은 개념이라 볼 수 있다.

리스트는 여러 개의 원소를 포함하는 하나의 집합체이다. 리스트에 포함되는 개별 값들을 원소라 할 수 있는데, 이 원소들은 모두 일정한 순서를 가지고 있다. 특정한 원소의 순서를 ‘인덱스’라고 한다.

리스트는 대괄호로 여러 값들을 연속해서 둘러 싸 만들 수 있다. 이 때 각 값들은 컴마(,)로 구분된다.

a  = [2,3,5,7,11,13,17,19]

a 라는 변수에 20보다 작은 소수(prime number)로 구성된 리스트를 만들어 대입했다. 이 때 작은 수 부터 큰 수의 순서로 쓴 것은 단지 편의의 문제일 뿐이다.

b = [5,19,2,7,13,11,3,19]

와 같이 불규칙한 순서를 써서 만든 리스트 b는 구성하고 있는 원소는 a와 같지만 각각의 원소의 순서가 다르다. (따라서 둘은 완전히 다른 리스트이다.)

하나의 원소의 인덱스

리스트의 각 원소는 정해진 순서가 있다고 했다. 이 때의 순서값을 ‘인덱스’라고 하며, 인덱스는 0부터 시작한다. 즉 첫번째 원소의 인덱스는 항상 0 이다.

리스트로부터 특정한 인덱스에 위치한 원소를 지정하려면 리스트이름[인덱스]와 같은 식으로 접근하게 된다.

print a[2]
#==> 5

마찬가지로 c = a[5] 와 같은 식으로 특정 원소를 다른 변수에 대입하는 것도 가능하다.

파이썬의 리스트는 재미있게도, 독특한 인덱스를 취급한다. 바로 음수 인덱스이다.

print a[2]
print a[-2]

위 명령은 리스트 a의 뒤에서 두 번째 원소를 가리킨다. 즉 17을 출력하게 된다.

리스트의 부분집합

리스트는 수학적 개념의 ‘집합’과도 매우 유사하다고 했다. 그리고 인덱스를 사용해서 특정한 원소에 접근하는 것이 가능하다고도 했다. 이와 마찬가지로 인덱스를 사용하여 부분 집합을 정의할 수 있다.

c = a[2:5]
print c

위의 코드는 리스트 a로부터 부분집합인 c를 추출하는 과정을 보여준다. a[2:5]는 2번째 인덱스에서 5번 인덱스까지를 말하는데, 주의할 것은 뒤쪽 인덱스는 포함하지 않는다. 즉 2번, 3번, 4번의 인덱스에 해당하는 원소만이 추출되는 것을 확인할 수 있다.

만약 :을 쓰고 한쪽을 비운다면 끝까지에 해당한다.

d = a[3:] # -> [7, 11, 13,19]
#인덱스           3   4   5  6
e = a[:5] # -> [2, 3, 5, 7, 11]
#인덱스           0  1  2  3   4

이 때도 뒤쪽 인덱스는 포함하지 않는 다는 점에 주의하자. 이는 range() 함수에서도 동일하게 적용되었다. range(2,10) 은 2에서부터 숫자 범위를 리스트로 만들어서 반환해 준다. 이 때 뒤쪽에 들어가는 10은 포함되지 않아서 9까지만 들어가게 된다. 여기서 중요한 것! 바로 range()  함수가 결과값을 list로 반환해준다는 것이다.

(시작값, 끝값) ==> range() ==> [시작값 ~ 끝 값]으로 된 리스트

문자열과 리스트

list는 리스트를 지칭하는 파이썬의 예약된 단어이다. “리스트라는 데이터 타입”을 의미한다. 같은 이름의 함수인 list()는 특정한 객체를 리스트로 만들어준다. 문자열은 한글자, 한글자의 문자가 이어져서 단어나 문장이 된 텍스트 정보를 의미하는데, 이는 한글자씩으로 만들어진 리스트와 비슷하지 않은가?

str = 'elephant'
g = list(str)
print g
# ['e','l','e','p','h','a','n','t']

문자열과 리스트를 오가는 표현은 이후로도 자주 등장할 것인데, 아마도 별도의 챕터로 분리해서 설명하는 것이 좋겠다는 생각이 든다.

list 는 파이썬에서 “리스트라는 데이터 타입”을 의미한다고 했다. 따라서  dir(list)라고 해보면 리스트가 가지고 있는 기능들을 열람할 수 있을 것이다.

dir() 명령을 통해 확인할 수 있는 리스트의 동작은 다음과 같다. (각각의 명령에 대해서 도움말은 help(list.pop) 과 같은 식으로 찾아볼 수 있다.

  • append(x) : 리스트의 끝에 새로운 원소 x를 추가한다.
  • count(x) :  리스트에서 x 라는 원소가 몇 번 들어있는 지 세어본다.
  • extend(x) : 리스트에 새로운 리스트 x를 연결해준다.
  • index(x) : 리스트에서 x라는 원소의 인덱스를 구해준다. 이 때 x가 두 개 이상 들어있다면, 맨 처음 x만 찾는다.
  • insert(인덱스, x) : 현재 리스트의 주어진 인덱스 위치에 x라는 원소를 끼워넣어준다.
  • pop() : 리스트의 맨 마지막 원소를 반환하고, 해당 원소를 원래 리스트에서 제거한다. 만약 pop(x) 라고 하면 x를 반환하고, 리스트는 x를 제거한다.
  • remove(x) : 리스트에 포함된 원소 x를 제거한다. (pop과는 달리 뭔가 반환하지는 않는다.)
  • reverse() : 리스트를 역순으로 바꾼다.
  • sort() : 리스트의 원소들을 정렬한다.

리스트를 다루는 함수

리스트 자체가 제공하는 함수는 ‘메소드’라고 부른다. reverse, pop, sort 등은 리스트의 메소드이다. 앞서 정의한 a의 경우에는 다음과 같이 실행해 볼 수 있다.

a.reverse()
print a # --> [19, 17, 13, 11, 7, 5, 3, 2]
a.pop()
#--> 2
print a #--> [19, 17, 14, 11, 7, 5, 3]
a.sort()
print a #--> [3, 5, 7, 11, 13, 17, 19]

이러한 리스트 자체의 메소드 외에도 몇 가지 리스트와 관련된 기본 함수들이 있다. 이 중에서 가장 자주 쓰이는 것은 len()sorted() 함수이다. len 함수는 리스트의 원소의 총 개수를 (즉 리스트의 길이를) 반환하고, sorted() 함수는 인자로 받은 리스트를 정렬한 사본을 반환한다. sorted() 함수는 원본 리스트의 원소의 순서를 바꾸지 않는다. 반면, 리스트의 list.sort() 메소드는 원본 리스트의 순서를 바꾼다.

그리고 list()라는 함수는 위에서도 잠깐 살펴보았지만, 리스트로 변경이 가능한 데이터형을 쪼개거나 변환하여 리스트로 만들어 반환한다. 문자열을 리스트로 바꾸거나 다른 “집합” 형식인 튜플을 리스트로 바꿀 수 있다.

리스트의 효용

다른 프로그래밍 언어에서도 배열은 매우 중요한 데이터 형식으로 취급한다. 일련의 데이터를 한 덩어리로 다루거나, 목록으로 관리하거나 하는 등 실질적인 어플을 만들 때 상당히 많이 적용된다.

또한 “스택”이나 “큐”와 같은 개념 역시 배열을 이용해서 구현한다. (스택이나 큐는 다른 글을 통해서 알아보도록 하자. 혹은 구글에서 검색을 해봐도 좋다.)

또한 문자열과 리스트를 서로 변환해가면서 처리하는 것 역시 매우 유용하게 활용된다.

다음 시간에는 리스트를 문자열과 어떻게 함께 사용하여 활용하는지, 그리고 조금 더 멋진 “지능형 리스트”란 무엇인지를 잠깐 살펴보겠다.

[Python101] 로직(2) – 반복문

지난 시간에는 조건을 통해 갈림길 중 한쪽으로 진로를 결정하는 조건문에 대해 살펴 보았다. 이번 시간에는 반복문에 대해 알아보도록 하겠다.

반복문은 쉽게 말해서 같은 (혹은 비슷한) 작업을 여러 차례 반복하도록 지시하는 구문이다. 파이썬의 반복문은 조금 독특한 구석이 있는데, 이는 설명을 하면서 이야기하도록 하겠다.

while 문

중고등학교 때로 기억을 거슬러 올라가보면 (요즘은 어떤지 모르지만) 그 때는 컴퓨터 시간에 BASIC을 배웠는데, 이 때도 FOR 문이라는 게 있었다. 그 이후로 반복문의 대명사는 사실 for 인데, 파이썬의 for는 약간 다른 (그리고 좀 멋진) 부분이 있어서 먼저 while 문을 살펴보도록 하겠다.

while 문은 어떤 조건이 있고, 이 조건이 참인 동안에는 지정된 명령을 계속해서 반복하도록 하는 구문이다. while 문은 다음과 같이 쓴다.

while 조건식:
    반복하는 명령

파이썬 인터프리터가 조건식을 만나면 조건식을 판별한다. 조건식의 결과가 참인 경우에는 반복 명령을 한 번 수행한 후 다시 조건식을 판별한다 결과가 참인 경우에는 이를 반복하고 거짓인 경우에는 더 이상 다음 명령을 수행하지 않고 그 다음 블럭으로 건너뛰게 된다.

다음은 가장 간단한 while 문의 예라 하겠다.

x = 1
while x =<40:
    print x
    x = x + 1

이 소스를 따라가보자. 먼저 x 에는 1이라는 값이 대입된다. 그런 다음 반복문을 만난다. x 는 40보다 작으므로, 3, 4 번 라인이 실행된다. 1이 출력되고 x 에는 x + 1 값(= 2)이 대입된다. 조건 식은 여전히 참이므로 계속해서 값이 출력된다. 40번째 반복에서 x = 41이 되고 더 이상 조건식을 만족하지 않으므로 반복구간 (반복 구간은 흔히 ‘루프’라고 부른다.)은 종료된다.

반복문의 중첩

반복문의 블럭 안에는 다른 반복문이 들어가도 된다. (물론 조건문이 들어갈 수도 있다.) 당연한 이야기를 써 놓으니 이상한데, 구구단을 출력하는 프로그램을 생각해보자

각 단은 *2, *3 … *9 한 값을 출력한다. 이는 반복문을 통해서 처리할 수 있다. 그리고 각 단은 2단 부터 9단까지 반복된다. 역시 반복문을 통해 처리한다. 즉 구구단 프로그램은 다음과 같이 구상할 수 있다.

  1. while 문을 통해 각 단의 값에 2~9를 곱해서 출력하도록 할 수 있다.
  2. 1의 작업을 다시 2~9단에 걸쳐서 반복한다.

실제 소스 코드는 다음과 같다. ex14.py는 while 문을 사용해서 구구단을 출력하는 프로그램이다.

#-*-coding:utf-8
#ex14.py
i = 2 # 각 단의 번호
while i < 10j = 2 # 각 행의 번호, 새로운 단을 시작할 때 2로 초기화한다.
    while j < 10:
         print "%d * %d = %d" % (i, j, i*j)
         j = j + 1
    print # 각 단이 끝나고 나면 한 줄을 띄운다.
    i = i + 1

소스를 분석해 보자

  1. 4행에서 i는 2단, 3단… 각 단을 나타내는 값이다. 구구단은 2단부터 시작하므로 2로 초기화한다.
  2. while 문을 통해  i 가 10보다 작을 때까지 반복한다. 반복문 안에는 각 단을 출력하는 부분이 들어가면 된다.
  3. 6행에서 다시 j=2로 초기화한다. 이는 각 단에서 매 행마다 곱해질 값이다.
  4. 7행은 while문을 통해 j=2,3,4…,9까지 반복하도록 한다.
  5. 중첩된 while문의 내부에서는 구구단의 각 행을 출력한다. 포매팅 문자열을 사용하여 깔끔하게 처리했다.
  6. 9행에서 한 줄의 문장을 출력한 다음에는 j에 1을 더한 값을 다시 대입한다.
  7. 이런 식으로 7~9행이 반복된다.
  8. 반복이 끝나면 11행을 만나는데 이 때 빈줄을 한 줄 출력한 다음, i에는 다시 1을 더해준다. 2단이 끝났으니 이제 3단을 출력할 차례이다.
  9. 위의 과정을 반복한다. 9단을 출력하고나면 맨 마지막에 i = 9 + 1 이된다. while의 조건을 만족하지 않으므로 반복문이 끝나게 된다.

말로 풀어서 설명해도 사실 명쾌하지는 않다. 대신 소스 코드를 보면서 하나 하나 추적해보면 어떤 식으로 흘러가는지 감이 올 것이다.

for 문

for 문은 조건을 사용하지 않는 반복문이다. (사실 다른 언어의 for 문은 대부분 조건을 사용한다.) for 문은 대신 “집합”을 사용한다. for 문의 사용방법은 다음과 같다.

for 변수 in 집합:
    *변수*를 사용하는 명령

파이썬의 for 문은 특별하다. 즉 어떤 집합에 대해 그 집합의 개개의 원소에 대해 같은 동작을 반복한다. 즉 어떤 조건을 만족하는 동안 반복하는 것이 아니라 집합의 원소에 대해 개별적으로 어떤 동일한 처리를 하고자 할 때 유용하다.

예를 들어서 1에서 500까지의 숫자를 모두 더하는 코드를 작성해보자.

sum = 0
for x in range(1,501):
    sum = sum + x
print sum

range()라는 함수가 처음나왔다. 대화형 쉘에서 help(range)해보면 알 수 있는데, 시작 값과 끝 값 사이의 범위를 “집합”으로 만들어주는 함수이다. 이 때 range() 함수의 끝 값은 실제로 범위에 포함되지 않는다. 따라서 range(1,501)은 [1,2,3,…,500] 까지의 집합을 나타낸다.

다시 for 문을 보자. x in range(1,501) 이라고 하였으니, 1,2,…500까지의 숫자 값이 x가 되어 매번 루프를 반복하게 된다.

다시 코드를 설명하면,

  1. sum은 합계값을 저장하는 변수이다. 0으로 초기화한다.
  2. 1~500 사이에 숫자에 대해 for 문으로 반복한다. 이 값들은 x에 대입된다.
  3. 매번 루프에서 x값을  sum에 더해서 sum에 대입한다. 즉 sum은 점점 값들이 쌓여간다.
  4. 루프를 다 돌고나면 sum을 출력한다.

이해가 좀 되었는지?

이 for 문을 사용하면 구구단 출력 프로그램은 훨씬 간단하게 작성할 수 있다.

for i in range(2,10):
    for j in range(2,10):
        print "%d * %d = %d" % (i, j, i*j)
    print

이제 위 코드를 읽고 무슨 말인지 이해할 수 있을 것이다.

“집합”

사실 파이썬에서는 iterable이라고 표현하는데, 이를 마땅히 번역할만한 마땅한 단어가 없어서 집합이라고 일단 해 두도록 한다. for 문은 이 집합의 각 원소를 돌아가며 반복하는 반복문이다. 파이썬에서 이 “집합”은 상당히 중요하다. for 문 뿐만 아니라 이런 집합들의 원소에 대해 개별적인 처리를 할 수 있는 방법들이 꽤 있으며, 이런 것들을 잘 활용하면 코드의 수를 크게 줄일 수 있다.

우선 반복문에 대한 기초적인 내용을 배웠으니, 다음은 정말 중요하다는 “집합”에 대한 내용을 조금 집중적으로 다뤄보도록 하겠다.

[Python101] 006 로직(1) – 조건과 조건문

사실, “자료의 유형”에 대해 먼저 다루어야 하는 게 아닌가 싶기도 하지만 그보다든 더 ‘일반적인’ 내용을 다루는 게 맞겠다는 생각에 로직을 먼저 설명하기로 결정했다. 자료의 유형은 사실 대부분의 프로그래밍 서적에서 맨 먼저 다루는 형태인데, 파이썬에서는 일부 매우 중요한 자료형을 제외하고는 ‘개념’만 이해하면 될 정도로 기본적인 내용은 단순하기 때문에 이 부분들은 차근차근히 알아 나가면 될 것 같아서 먼저 로직에 대해 설명해보기로 하겠다.

프로그램은 사람이 정의해준 절차에 따라 컴퓨터가 연속적으로 일을 처리해 나가도록 하는 일종의 “작업 지시서”이다. 당연하게도 파이썬은 소스코드에 명시한대로 일을 처리해 나간다. 하지만 항상 모든 프로그램이 “정해진 일을 순서대로 처리하기만”하는 것은 아니다. 특정한 조건에 따라서 조금씩 다르게 일을 처리해야 하는 경우가 있다. 또한 반복적인 처리를 언제까지 혹은 몇 번이나 반복해야 하는지를 매번 판단해야 하는 상황이 있을 수 있다.

즉 프로그램의 흐름(flow)은 항상 직선으로 나아가는 것이 아니라 많은 상황이나 조건에 따라서 갈림길 중 하나를 택해서 진행되거나, 같은 코스를 수차례 뱅글뱅글 돌게되는 상황이 있을 수 있는 것이다. 이러한 프로그램의 흐름을 결정하는 원리를 “로직”이라고 부른다. 그리고 이 “로직”은 기본적으로 “논리적 판단”을 따르게 된다.

논리적 판단, 참과 거짓

이 때 말하는 논리적 판단이란 아주 간단히 “참”이냐 “거짓”이냐 그 자체이다. 컴퓨터는 이 두가지 경우만은 판단할 수 있다. 모든 것이 딱 부러지게 참 아니면 거짓이되는 것이다. 파이썬에서 참은 True 거짓은 False라고 정의되어 있다. 그리고 이런 참, 거짓을 계산(?)하기 위해서 비교 연산과 논리 연산이라는 것을 하게 된다.

True 와 False 는 참고로 파이썬에는 0, 1 처럼 미리 약속되어 있는 값이다. False는 0 또는 None 과 유사한 개념으로 이해하면 된다. True는 1 혹은 0이 아닌 어떤 것이 될 수 있다.

비교 연산

참이다 거짓이다라는 것은 수학 시간에 배우는 명제의 참과 거짓과는 닮았으면서도 비슷하다. 논리적으로 참과 거짓을 결합하여 계산하는 방식은 수학의 그것을 닮았지만, 컴퓨터는 사람의 언어로 만들어진 명제를 이해하지 못한다. 다만 “3은 2보다 크다”라는 명제를 “3 > 2″라는 수식으로 써 줄 때 이를 계산하여 그것이 참인지 거짓인지를 판별할 수 있는 것이다. 결국 어떤 값이 어떤 값과 같다 / 크다 / 작다를 판별하는 것만이 판단의 기준이 된다.

이러한 판단을 위해서 비교 연산자라는 것을 사용한다. 비교 연산자에는 다음과 같은 것들이 있다.

  • == : 등호(=)를 2개 연속으로 사용하여 A == B 와 같은 형식으로 사용한다. 실제로 A 와 B가 동일한 값이라면 이 식은 참(True)이 된다.
  • >, >= : 크다와 크거나 같다를 의미한다. A >= B 는 A>B 이거나 A==B이면 참이 된다.
  • <,<= :  작다와 작거나 같다를 의미한다.
  • != : 같지 않다는 의미이다.

대화형 쉘에서 다음 수식들을 평가해 도록 하자. 수식을 입력하고 엔터를 치면 True 혹은 False로 결과값이 출력된다. 수식을 실제로 실행하여 평가하기 전에 결과가 True 인지 False인지 먼저 생각해보고, 실행한 결과가 자신의 생각과 맞는지 확인해보자.

  • 1 == 13
  • 13 == “13”
  • 4 > 5
  • -2 <= -2
  • 2 == 2
  • -4 * 0 >= 0
  • ‘Hello’ == “Hello”

논리연산자

비교 연산자는 쉽다. 하지만 현실은 그리 녹록하지 않다. 단순비교를 넘어서 많은 경우에 2개 혹은 3개 가량의 조건을 동시에 고려해야 하는 경우가 많다. 논리 연산자는 여러 개의 조건을 동시에 평가하는데 사용한다. 예를 들자면 가스 밸브의 압력이 10~20 사이일 때가 정상이라고 할 때 가스 밸브의 압력이 정상인지를 판단하기 위해서는 다음의 두 조건을 동시에 만족해야 한다.

  1. 밸브압력 >= 10
  2. 밸브압력 <= 20

이 두 개의 조건을 하나로 묶어주는 것이 논리 연산자이다. 논리 연산자는 역시 의외로 간단하다.

  • AND : A AND B – A도 참, B도 참인 경우에만 참이된다. A가 참, B가 거짓인 경우에는 A AND B는 거짓이다. 즉 2개의 조건이 모두 참인 경우만 참이 된다.
  • OR : A OR B – A, B 둘 중의 하나만 참이면 참이다. OR은 “~ 혹은 ~”의 뜻이므로 글자 그대로 생각하면 된다.
  • NOT A : A의 논리값을 뒤집는다. A가 참이면 NOT A는 거짓, A가 거짓이면 NOT A는 참이된다.

그리고 비교 연산자와 논리 연산자들을 결합하여 보통 우리는 “조건”을 만들어 낼 수 있다. 하나의 조건은 비교 연산을 통해 참/거짓으로 구분되는 식이며, 여러 개의 조건은 다시 논리 연산자를 통해 하나의 조건으로 합성될 수 있다.

위에서 언급한 가스 밸브의 경우, 조건식은 다음과 같이 만들어진다.

{밸브 압력} >= 10 and {밸브 압력} <= 20

밸브의 압력이 10~20 사이의 값일 때만 두 식을 모두 만족하게 되는 것이다.

자 이제 다시 다음 20개의 식의 결과가 True 인지 False 인지 각각 생각해보고 대화형 쉘에서 실행하여 그 결과를 확인해 보라.

  1. True and True
  2. False and True
  3. 1 == 1 and 2 == 1
  4. “test” == “test”
  5. 1 == 1 or 2 != 1
  6. True and 1 == 1
  7. False and 0 != 0
  8. True or 1 == 1
  9. “test” == “testing”
  10. 1 != 0 and 2 == 1
  11. “test” != “testing”
  12. “test” == 1
  13. not (True and False)
  14. not (1==1 and 0 != 1)
  15. not (10==1 or 1000 == 1000)
  16. not (1 != 10 or 3 == 4)
  17. not (“testing” == “testing” and “Zed” == “Cool Guy”)
  18. 1 == 1 and not (“testing” == 1 or 1 == 0)
  19. “chunky” == “bacon” and not (3 == 4 or 3 == 3)
  20. 3 == 3 and not (“testing” == “testing” or “Python” == “Fun”)

조건문

조건식에 대해서 기본적인 내용을 (사실 저게 전부임) 알아보았으니, 조건에 따른 분기를 어떻게 만드는지 살펴보도록 하겠다.

거의 모든 프로그래밍 언어는 조건문을 처리할 수 있는 문법을 가지고 있고, 대부분의 언어가 if….를 사용한다. 파이썬도 마찬가지로 if 문을 사용한다. 파이썬의 if 문의 사용 방법은 다음과 같다.

if 조건 :
    조건이 참일 때 실행할 문장 #그리고 앞에는 들여쓰기를 한다.

if 다음에 조건 식을 쓴 다음 콜론(:)으로 끝난다. 그런다음, 조건이 참일 때 동작할 문장들을 들여쓰기 해서 써 준다. if 구문의 완료 지점은 들여쓰기를 하지 않는 라인을 만날 때 까지이다.

위의 if 문을 사용하면 조건이 참일 때에만 특정한 명령들을 실행할 수 있게 된다. 만약, 조건이 참일 때는 A라는 동작을 하고 조건이 거짓일 때는 B라는 동작을 수행하도록 하려면 어떻게 할까? 이 때는 else 문을 쓴다.

if 조건:
    조건이 참일 때 실행할 명령 ...
else:
    조건이 거짓일 때 실행할 명령 ....

그리고 많은 경우에는 조건 1, 조건 2, 조건 3… 등 각각의 경우에 따라 다른 동작을 하고 싶을 때도 있을 것이다. (예를 들어 점수의 영역에 따라 수우미양가를 매길 때처럼) 이 때는 다음과 같이 elif를 사용하여 제2, 제3의 조건을 붙여 나갈 수 있다.

if 조건1:
    조건 1이 참일 때 실행할 명령
elif 조건2:
    조건 1이 거짓일 때, 다시 조건 2를 검사하여 조건2가 참일 때 실행
elif 조건3:
    다시 조건 3일 때 명령
...
else:
    위의 모든 조건이 거짓일 때 실행하는 명령

만약 어떤 점수에 따라 학점을 A,B,C,D,F 로 나눠주는 코드를 작성한다면 다음과 같이 될 것이다.

#ex12.py
#-*-coding:utf-8

def checkGrade(point):
  if point >= 90:
    return "A"
  elif point >= 80:
    return "B"
  elif point >=70:
    return "C"
  elif point >=50:
    return "D"
  else:
    return "F"

myPoint = 85

print "My Point is %d, so my grade is %s" %(myPoint, checkGrade(myPoint))

ex12.py는 지금까지 배운 내용들은 나름 잘 요약하고 있는 예제라 할 수 있겠다. 먼저 점수에 따라 학점을 판별하는 처리는 checkGrade()라는 함수를 만들어서 처리한다.

이 함수는 조건문을 계속 사용하여 90점 이상인 경우에는  A, 80점 이상인 경우에는 B…와 같은 식으로 등급을 문자열로 반환해준다. 함수가 return 문을 만나면 결과값을 반환하고 더 이상 실행되지 않는다.

그리고 myPoint에 점수를 대입한 뒤, 점수와 그 처리 결과를 하나의 문장에서 print 하도록 했다.

조건문은 단순히 조건식을 평가하여 참/거짓의 여부에 따라 갈림길의 방향을 선택하는 장치라고 볼 수 있으며, 다양한 상황을 유연하여 처리할 수 있게끔하는 기본적인 프로그램의 논리 처리이다. 프로그램은 이 조건문과 다음에 배울 반복문을 통해 사람이 일일이 간섭하지 않고 (내지는 사람에게 매번 귀찮게 물어보지 않고) 알아서 많은 처리를 빠르게 할 수 있게 된다.

다음 순서는 반복문에 대해 배워보도록 하겠다. 그리고 반복과 조건을 결합하여 할 수 있는 몇 가지 재밌는(?) 실험들을 조금 해 볼 것이다.

[Python101] 005 함수,모듈, 그리고 도움이 되는 도구들

지난 시간까지 기본적인 프로그래밍에 필요한 “입출력”을 다루는 부분을 간단하게 나마 살펴보았다. 실제로 지금까지의 내용은 뭔가 설명이 필요하거나 개념적인 이해를 도모하는 부분과는 조금 거리가 있었고, 마치 조리법처럼 “이렇게 하면 이렇게 이렇게 됩니다.” 정도였기에 경우에 따라서는 조금 재미도 떨어지고 지겨운 부분이 없지 않아 있었을 것으로 생각된다.

이번 시간부터는 프로그램을 구성하는 단위와 이를 어떻게 만드는지, 그리고 프로그램의 흐름은 어떤 식으로 만들어지는지를 살펴보고자 한다.

모듈

모듈(Module)이라는 것은 다른 것으로 대체 가능한 어떤 구성 요소를 뜻하는데, 파이썬에서는 미리 만들어두었다가 필요할 때 꺼내 쓰는 레고 조각같은 프로그램의 조각을 말한다. 보통 프로그램의 소스 코드가 몇 백줄에서 몇 천줄까지 커지는 크고 복잡한 프로그램을 만든다면 이를 하나의 파일에 모두 작성하게 되면 사소한 오타로 인해 발생하는 에러를 잡기도 힘들어지고, 나중에 어떤 부분을 수정해야할 때 큰 범위를 일일이 찾아야 하므로 상당히 불편하고 어려워진다. 파이썬 프로그램 소스는 그 단위 단위가 모두 ‘모듈’이고, (뒤에서 설명할) 단위 프로그램 내의 함수나 클래스 같은 것들도 한 편으로는 모듈이라고 할 수 있다.

예를 들어 운영 체제에서 어떤 파일이나 폴더가 있는지를 검사하고, 파일을 복사, 삭제하거나, 폴더에 파일이 몇 개나 들어있는지를 알아내는 함수들을 만들었다고 하자. 나중에 이런 기능을 다른 프로그램에서도 사용해야 한다면, 그 때가서 똑같은 기능을 일일이 구현하는 것은 상당히 번거로운 일이 될 수도 있다.

그래서 이런 여러 개의 함수를 path(경로)라는 모듈로 묶어준다. 그리고 이런 경로와 관련된 것외에 운영체제의 특성이나 명령과 관련되는 모듈들을 다시 묶어서 os라는 모듈로 만들어둘 수 있겠다. 그리고 이게 파이썬이 기본적으로 제공하는 os 라이브러리의 정체가 된다.


from os.path import exists

라는 구문을 통해서 파일의 존재여부를 알아낼 수 있는 exists()라는 함수를 이미 사용해 본 바가 있다. (이는 우리가 구현한 것이 아니라 이미 구현되어 있는 함수를 가져다 사용한 예이다.)

파이썬을 설치하면 기본적으로 함께 설치되는 표준 라이브러리에도 활용가치가 높은 모듈들이 많이 있으며, 또 이미 많은 개발자들이 유용한 모듈들을 만들어서 공개해두고 있어 엄청나게 많은 활용 가능한 모듈들이 있다. 그래서 파이썬으로 작은 유틸리티를 만드는 일은 무척이나 쉬울 수 있다. (거의 필요한 기능들은 찾아보면 다 만들어져 있어서) 마치 레고로 자동차를 조립하는 것처럼 핸들, 바퀴, 차대 등의 부속품을 import 하여 필요한 모듈들이 함께 동작하도록 만들어주면 되는 것이다.

import / from… import

지금 편집하고 있는 소스코드 파일이 아닌 다른 곳에서 모듈을 가져와서 사용할 때 import 명령을 사용한다. import 명령은 다른 모듈 파일의 전체 혹은 일부를 현재 프로그램으로 반입해 오는 명령이다. 반입한 객체는 마치 미리 정의한 것과 같은 효과를 갖게 된다. 덕분에 프로그램 소스 코드가 간결해지고, 만들어야 하는 코드가 적어진다. (게다가 이렇게 모듈로 만들어둔 프로그램은 다른 프로그램에서 쉽게 재사용할 수 있게 된다.)

또한 모듈로 작성된 파일 내에서 그 일부만 반입해오는 경우에는 from 모듈이름 import 실제 반입할 모듈과 같은 식으로 코드를 반입할 수 있다.

모듈을 통째로 반입한 경우에는 그 하위에 있는 함수를 사용할 때 모듈.함수()와 같은 식으로 . 구분자를 써서 이들을 연결해 줘야 하지만 from 모듈 import 함수한 경우에는 함수 이름을 그대로 사용해주면 된다.

실제로 모듈을 만들어서 사용하는 예는 이 글의 말미에서 다시 다루기로 하고, 이번에는 함수에 대해 알아보도록 하겠다.

함수

함수는 프로그래밍에서 가장 중요한 개념 중의 하나이다. 함수는 “가장 작은 온전한 프로그램의 최소 단위”이다. 이 말이 무슨 뜻인고 하니, 우리가 흔히 ‘함수’라고 하면 y=2x+5와 같이 중고등학교 수학시간에 배운 함수를 떠올리게 된다. 음… 맞다. 함수는 딱 저렇게 생겨서 동작하게 된다. 여전히 감이 잘 오지 않는데, 이 함수를 잘 살펴보도록 하자.

x = 1 을 넣으면 y = 7이 된다.` x = 2` 를 넣으면 y = 9가 된다. 즉 어떤 값을 입력 (x에 대입)해 주면 이 값을 처리하여 그 결과값을 돌려주고 있다. 그리고 그 결과는 “입력된 값을 2배하고, 거기에 5를 더한다”는 처리를 한 결과값이다.

프로그래밍에서의 함수는 “가장 작은 프로그램의 단위”라고 했다. 프로그램 자체가 입력을 받아 이를 처리하고 그 결과를 출력해주는 기계 장치이므로, 이러한 프로그램의 특성을 함수는 그대로 가지고 있다. 그래서 프로그램 그 자체도 함수이며, 프로그램은 작고, 많은 함수들이 결합된 형태로 만들어진다.

그럼 어떤 기능을 함수로 만들면 좋을까? 답은 “반복적으로 하게 되는 작업”을 함수로 만들면 된다. 그 역시 감이 잘 오지 않는다면 “함수는 타이핑을 줄여주는 테크닉“이라고 처음에 생각해도 크게 무리가 없다.

함수 만들기

실제로 함수를 한 번 만들어 보자. 함수를 만드는 키워드는 def 이다. 함수는 다음과 같이 생성한다.

def 함수이름(인자값, ...):
    # << 어떤 동작을 하는 코드들 >>
    return 반환값 

def 키워드 다음에 함수의 이름을 쓴다. 그런 다음 함수가 받는 인자값의 이름을 써준다. 문장의 끝은 콜론으로 끝나고, 실제 함수가 해야 하는 동작은 모두 들여쓰기 해준다. 위에서 이야기한 y = 2x + 5를 파이썬 함수로 만들어보자.

def doublePlusFive(x):
    y = 2*x + 5
    return 5

print(doublePlusFive(1)) #==> 7
print(doublePlusFive(2)) #==> 9
print(doublePlusFive(5)) #==> 15

물론 위의 예제에서는 함수를 만드는 예를 보이다보니 저럴 거면 왜 함수를 쓰느냐고 할 수 있는데, 만약 섭씨나 화씨 온도를 서로 변환해야 하는 경우를 생각해보자. 섭씨온도를 화씨 온도로 바꾸는 식은 다음과 같다.

℉ = ℃ * 9 / 5 + 32

반대로 화씨 온도를 섭씨온도로 바꾸는 식은 다음과 같다. (위의 식을 바꾼 것에 지나지 않는다.)

℃ = (℉ - 32) * 5 / 9

그럼 화씨와 섭씨를 각각 바꿔주는 함수는 다음과 같이 작성할 수 있을 것이다.

#ex12.py
def FtoC(fh):
    return (fh-32) * 5 / 9.0

    def CtoF(cd):
        return cd*9/5.0 + 32

이렇게 함수로 만들어두면 나중에는 FtoC(45) 라고만 쓰면 화씨 45도가 섭씨 몇 도에 해당하는지 쉽게 알 수 있게된다. 만약 코드상으로 저 공식을 바로 사용하는 것도 “똑같은” 방법이라 할 수 있겠지만 1) 시간이 지나고 나서는 이 공식이 뭔지 기억이 안날 수도 있고, 2) 나중에 다시 같은 공식을 써야할 때 공식이 생각나지 않는 경우가 생길 수 있다.

대신에 함수를 사용하면 1)함수의 이름으로 이게 무슨 처리를 하는지 유추할 수 있고1 2) 함수를 모듈로 만들어 놓으면 굳이 공식을 기억하지 않아도 함수를 바로 사용할 수 있다.

함수와 모듈

위의 소스 코드를 ex12.py로 저장하고 이를 모듈로 사용하는 ex13.py를 새로 작성해보도록하자.

#ex13.py
#이 파일은 ex12.py와 같은 폴더에 저장해야 함.

import ex12

celsius = 25
farenheit = 45

print("섭씨 %d도는 화씨로 %f입니다." % (celsius, ex12.CtoF(celsius))
        print("화씨 %d도는 섭씨로 %f입니다." % (farenheit, ex12.FtoC(farenheit))

ex13.py는 ex12.py의 내용을 ex12라는 이름의 모듈로 입수한다. 우리가 이미 작성한 ex12.py 파일내의 모든 내용이 이 부분에 덧붙는 다고 생각하면 된다.

또한 우리가 반입한 내용이 ex12 전체이기 때문에 그 속에 들어있는 함수를 사용할 때는 ex12.FtoC(), ex12.CtoF()와 같은 식으로 반입한 모듈의 이름을 앞에 붙여주고 점(.)으로 구분해 준다. 만약 FtoC(), CtoF()의 이름을 그대로 사용하고 싶다면 다음과 같이 import 구문을 바꿔주면 된다.

from ex12 import FtoC
from ex12 import CtoF

모듈의 위치

모듈로 반입해 올 수 있는 파일의 위치는 특정한 범위로 제한된다. 현재 실행되는 프로그램 소스와 같은 폴더에 있거나, 파이썬의 표준 모듈 폴더에 있어야 한다. 이 표준 모듈 폴더는 sys.path라는 곳에 저장되어 있는데 여기에 특정한 폴더를 추가해 줄 수도 있다. 이 방법에 대해서는 나중에 다시 살펴볼 기회가 있을 것이다.

대화형 쉘 활용하기

지금까지 대화형 쉘은 그저 “계산기” 정도의 용도로 쓴 것이 사실이다. 단순히 하나의 명령이 어떤 일을 하는지 이미 알고 있는 상황에서 print 명령을 쓴다거나 하는 일이 그리 ‘효율적’으로 느껴질리도 없다. 지금부터는 대화형 쉘을 어떻게 활용할 수 있는지 살펴보겠다.

함수를 테스트하기

대화형 쉘은 기본적으로 한 번에 한 줄의 명령을 실행할 수 있도록 만들어져 있다. 즉 명령문을 입력하고 엔터키를 누르면 해당 명령이 바로 실행되어 즉시 그 결과를 볼 수 있는 것이다.

하지만 함수를 만드는 def 구문을 쓸 때는 엔터를 누르더라도 바로 실행되지 않는다. 위에서 예를 들었던 doublePlusFive() 함수를 대화형 쉘 모드에서 입력해보라. def doublePlusFive(x): 하고 엔터를 누르면 프롬프트가 나오지 않고 들여쓰기 상태가 된다. 이 상태에서 함수의 나머지 코드를 입력하고, 엔터를 두 번 누르면 결과가 출력되지 않고 다시 프롬프트가 표시된다.


>>> def doublePlusFive(x):
y = 2*x+5
return y

>>> doublePlusFive(7)
19

다시 프롬프트에서 doublePlusFive(7)을 입력하고 엔터를 치면 해당 함수가 실행되어 계산 결과를 표시하게 된다. 즉 대화형 쉘은 메모리에 만들어진 수식이나 읽어들인 외부 모듈을 계속 가지고 있으므로 간단한 ‘테스트’를 해보는 데 큰 도움이 된다. 특히 앞으로 배우게 될 리스트와 관련하여 애매한 부분들은 대화형 쉘에서 간단히 시험해봄으로써 정확한 명령문을 결정하는 데 도움이 될 수 있다.

모듈 함수의 도움말을 보기

help() 명령은 특정한 모듈 혹은 함수의 도움말을 표시해주는 명령이다. 백문이 불여일견, help(max)라고 대화형 쉘에서 실행해보자. max()는 이름에서 유추할 수 있지만 최대 값을 구하는 함수이다. 이 함수의 help 결과는 다음과 같다.


>>> help(max)
Help on built-in function max in module __builtin__:

max(...)
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value

With a single iterable argument, return its largest item.
With two or more arguments, return the largest argument.

help에서 표시하는 정보는 max__builtins__ 라는 특별한 이름의 모듈에 속해 있는 함수임을 알 수 있고, max(a, b, c, d...) 이런 식으로 쓰면 그 중에서 가장 큰 값을 반환한다는 설명을 볼 수 있다.

그러면 한 번 테스트해보자.


>>> someNumbers = (1,2,3,4,5)
>>> max(someNumbers)
    5

someNumber라는 변수에 (1,2,3,4,5) 라는 숫자 묶음을 대입해주고, max를 통해 someNumber 중에서 가장 큰 숫자를 구하는 명령을 실행해보면 최대 값을 구해 반환하는 것을 볼 수 있다.

모듈이 어떤 기능을 수행할 수 있는지를 살펴보는 방법

우리는 방금 ‘최대값’을 구하는 함수를 어떻게 사용하는지 살펴보았다. 그렇다면, 반대로 최대값을 구하는 함수도 있지 않을까? 이름으로 유추해보자면 min()이라는 함수가 있을 것 같다. 그럼 그런 함수가 있는지 없는 지 어떻게 알 수 있을까?

이럴 때 유용하게 사용할 수 있는 함수가 있다. 바로 dir()이라는 내장 함수이다. 이 함수는 인자로 받은 모듈에 포함되어 있는 하위 기능을 모두 열거해준다.

우리는 조금전 help(max)에서 이 기능이 “builtins“라는 특별한 모듈에 속해있다고 하는 것을 보았다. 양끝에 언더바(_)가 두 개씩 붙은 것은 “내부적으로”라는 의미라고 이해하면 되고 실제 글자로는 built-in된 모듈이라는 의미이다. dir 함수는 모듈의 내부 구성 요소를 볼 수 있는 명령이라 하였으니 이 builtins 모듈의 내부에 정말 min 이라는 함수가 있는지 살펴보도록 하자.


>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', 'BufferError', 'BytesWarning',
'DeprecationWarning', 'EOFError', 'Ellipsis',
'EnvironmentError', 'Exception', 'False', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
'ImportWarning', 'IndentationError', 'IndexError', 'KeyError',
'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError',
'None', 'NotImplemented', 'NotImplementedError', 'OSError',
'OverflowError', 'PendingDeprecationWarning', 'ReferenceError',
'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration',
'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit',
'TabError', 'True', 'TypeError', 'UnboundLocalError',
'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError',
'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError',
'_', '__debug__', '__doc__', '__import__', '__name__',
'__package__', 'abs', 'all', 'any', 'apply', 'basestring',
'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable',
'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex',
'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod',
'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter',
'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
'hash', 'help', 'hex', 'id', 'input', 'int', 'intern',
'isinstance', 'issubclass', 'iter', 'len', 'license',
'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min',
'next', 'object', 'oct', 'open', 'ord', 'pow', 'print',
'property', 'quit', 'range', 'raw_input', 'reduce',
'reload', 'repr', 'reversed', 'round', 'set', 'setattr',
'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

여기서 표시되는 이름들은 거의 모두가 내장 함수 혹은 내장 모듈들이다. 앞쪽에 대문자로 시작하는 것들은 거의 내장 모듈이나 상수들을 의미하며, 중간에 __로 둘러 싸여진 이름은 모듈 내에서 다시 “내부적으로” 사용되는 기능이다. 마지막에 abs… 부터가 실제 우리가 자주 사용하는 내장 함수 들의 이름이다.

이러한 내장 함수의 이름이나 사용법을 절대로 다 외울 필요가 없다. 앞으로 공부를 해 나가면서 필요한 함수들은 그만큼 자주 사용하게 될 것이니 자연스럽게 익히게 될 것이다. 대신, 뭔가 새로운 모듈이나 함수를 앞으로 접하게 된다면 dir 및 help 명령을 통해 사용법을 확인하고 다른 연관된 명령이 있는지 살펴보는 것은 큰 도움이 된다. 반드시 이런 식으로 공부를 해 나갈 것을 권한다.

1 그래서 함수의 이름을 잘 정하는 것은 엄청나게 중요한 일이다.

[Python101] 004. 파일. 파일을 읽고, 파일에 쓰기

사용자로부터 입력을 받아 데이터를 처리하는 프로그램은 실질적으로 효율에 한계가 있다. 처리해야 하는 데이터를 사용자가 일일이 매번 입력해야 하기 때문이다. 컴퓨터는 귀찮고 반복적으로 처리하는 작업을 수월하게 하라고 있는 것이므로 프로그램은 가능한한 많은 과정을 자동화하는 방향으로 처리하는 것이 좋은 경우가 많다.

보통의 경우에는 다음과 같은 방식의 순서로  작업을 많이 처리하게 된다.

  1. 처리해야 할 데이터를 미리 준비 (텍스트 파일이나 엑셀 파일 등)
  2. 프로그램을 실행할 때 매개변수로 처리할 파일을 주고 실행한다.
  3. 프로그램이 데이터 파일을 읽어 들여서 데이터를 주르르르륵 처리한다.
  4. 처리한 결과는 또 다른 파일에 기록되거나 화면에 출력된다.

이것은 일종의 자동화 작업이고, 파이썬은 이런 작업에 대한 처리를 쉽고 빠르게 처리하기에 매우 좋다.

파일 읽기

데이터를 저장한 파일로부터 데이터를 읽는 방법에 대해 먼저 살펴보도록 하자. 예를 들어 메모장으로 작성한 텍스트 파일의 내용을 읽어들이고, 뭔가 내용을 쓰는 작업은 다음과 같은 절차를 거친다.

  1. 파일을 연다. 파일을 열게 되면 파이썬은 이 파일을 다루기 위한 핸들러(Handler)를 사용하게 된다.
  2. 파일의 내용을 읽어들인다. 파일의 내용을 읽어들이는 작업은 파일을 열면서 생성한 핸들러가 중계해준다. 핸들러는 파이썬 스크립트와 실제 데이터 파일 사이를 중계해주는 모듈이라 생각하면 된다.
  3. 파일에는 새로운 내용을 쓰거나, 기존 파일의 내용 뒤에 내용을 추가하는 것도 가능하다. 이 역시 핸들러에 의해 데이터를 쓸 수 있다.
  4. 파일 다 사용했으면 파일을 닫는다. 만약 파일을 적절히 닫지 않으면 파이썬 프로세스가 정상적으로 종료되지 못하거나, 다른 프로그램이 해당 파일을 쓰지 못하는 경우가 발생할 수 있다.

파일을 열기 위해서는 open 명령을 사용한다. open 명령은 다음과 같이 사용한다.

파일핸들러 = open('파일의 경로', [모드])

open  명령을 사용해서 파일을 열면 파일의 핸들러를 얻을 수 있다. 이 핸들러를 통해 파일의 내용을 읽어들이거나, 파일에 데이터를 쓰는 작업이 가능해진다.

ex09.py

아홉번째 예제는 텍스트 파일을 열어서 그 내용을 보는 예제이다.이 예제를 제대로 실행하기 위해서는 텍스트 파일 하나가 필요하다. 메모장을 열어 아무 텍스트나 몇 줄 입력한 후에 예제를 저장하는 폴더에 저장한다. 이름은 구분하기 쉽게 ex09.txt로 하겠다.

April

Still an irritating wind;
Vestiges of stubborn grey –
Jibing us of recent winter blight.

It’s coming though – like perky breasts
Pushing through a blouse –
Teasing, pleasing in it’s tantalising play:

Warmth of youth in April sun –
Simmering off depression,
Brains retuned; remapped for fun.

April is a portal –
Smoothly transitions
Delicate dispositions – suchlike mine,

Easing hunched bodies into
Summery smiles.

이제 ex09.py 의 소스 코드는 다음과 같다.

from sys import argv

script, filename = argv

txt = open(filename)

    print "Here's your file %r:" % filename
print txt.read()

    print "Type the filename again:"
    file_again = raw_input('> ')

txt_again = open(file_again)
    print txt_again.read()

argv 가 등장하는 것을 보니, 파라미터를 받고 실행하는 프로그램이다. 실행은 앞의 강좌에서 소개했던 것과 같은 방식으로 하면 된다.

 > python ex09.py ex09.txt 이렇게 실행한 결과는 다음과 같다.

Here's your file 'ex09.txt':
April

Still an irritating wind;
Vestiges of stubborn grey
Jibing us of recent winter blight.

It’s coming though  like perky breasts
Pushing through a blouse
Teasing, pleasing in it’s tantalising play:

Warmth of youth in April sun
Simmering off depression,
Brains retuned; remapped for fun.

April is a portal
Smoothly transitions
Delicate dispositions ? suchlike mine,

Easing hunched bodies into
Summery smiles.
Type the filename again:
> ex09.py
from sys import argv

script, filename = argv

txt = open(filename)

print "Here's your file %r:" % filename
print txt.read()

print "Type the filename again:"
file_again = raw_input('> ')

txt_again = open(file_again)
print txt_again.read()

자 이제 코드를 좀 살펴보자.

1) 3행을 보면 파일 이름을 argv로부터 읽어온다.

2) 5행에서는 이 파일 이름을 사용하여 해당 파일을 연다. 파일 이름만 전달 받았으므로 파이썬 코드 파일과 같은 폴더 내의 파일을 탐색하게 된다. 그리고 파일을 열어서 그 핸들러는 txt라는 이름의 변수에 대입된다.

3) txt는 곧 파일 핸들러라고 했다. 파일 핸들러는 여러 기능 모듈을 가지고 있는데 그 중 하나가 read() 이다. 이 모듈은 파일의 내용을 전부 읽어들이는 일을 한다.

이 때 txt 모듈의 read() 를 실행하기 위해서는 구두점(.)으로 이를 연결해준다. 즉 txt.read() 는 txt 모듈의 read라는 기능 모듈을 실행한다는 뜻이다. 실행이 가능한 모듈은 주로 ()가 뒤에 붙는데, 이렇게 실행이되는 모듈을 특별히 ‘함수’ 혹은 ‘메소드’라고 한다.

txt.read() 모듈은 파일 전체의 내용을 읽어 이를 돌려준다. 8행은 이 결과를 모두 출력하게 된다.

4) 10행에서는 다른 파일 이름을 다시 물어본다. 나는 ex09.py를 입력했다. 파이썬 스크립트 소스 자체도 텍스트 파일이다. 단지 관습적으로 .py 라는 확장자를 썼을 뿐이다.

5) 주목할 것은 마지막의 14행이다. txt_again은 파일을 열고 그 핸들러를 대입하게 된다. 즉 txt_again이 파일의 핸들러이고, open() 이 실행된 결과와 동일하다. 즉 open()의 결과가 파일 핸들러라는 이야기다. 따라서 13, 14행은 다음의 한 행으로 합칠 수 있다.

print open(file_again).read()

파일에 쓰기

ex09.py는 파이썬에서 다른 텍스트 파일의 내용을 읽어와서 사용할 수 있음을 보여주었다. 주소록 정리라든지, 성적 계산, 혹은 이름-이메일 정보를 가진 주소록을 가져와서 받는 사람 주소에 맞게 사람 이름이 들어가는 이메일을 보내는 프로그램 등을 만들 수 있을 것이다.

이번에는 내용을 파일에 기록하는 작업에 대해 살펴보자. 지금까지는 open() 명령에서 모드를 따로 지정하지 않았다. 모드를 따로 지정하지 않은 open()은 기본적으로 “텍스트 파일”을 “읽기”모드로 열게 된다. 파일을 기록하기 위해서는 파일을 w 모드로 열면 된다. w 모드로 파일을 여는 것은 사실 새로운 파일을 만들고 그곳에 내용을 기록하는 것을 의미한다. 따라서 파일 이름이 기존에 있던 내용이면, 그 파일을 덮어쓰게(overwrite) 된다는 점을 참고하자.

ex10.py는 사용자로부터 3줄의 텍스트를 입력받아 이를 파일에 기록해주는 프로그램이다.

from sys import argv

script, filename = argv

print "We're going to erase %r." % filename
print "If you don't want that,hit Ctrl-C(^C)."
print "If you do want that, hit RETURN."

raw_input('?')

print "Opening the file..."
target = open(filename, 'w')

    print "Truncating the file. Goodbye!"
target.truncate()

    print "Now I'm going to ask you for three lines."

    line1 = raw_input("Line 1:")
    line2 = raw_input("Line 2:")
    line3 = raw_input("Line 3:")

    print "I'm going to write these to the file."

target.write(line1)
    target.write('\n')
target.write(line2)
    target.write('\n')
target.write(line3)
    target.write('\n')

target.close()

코드를 보면 알겠지만, 텍스트 파일의 이름을 파라미터로 받고 있다. 따라서 콘솔에서 파일을 실행할 때 텍스트 파일의 이름을 준다. 이 코드에는 파일의 내용을 지우는 truncate() 명령이 포함되어 있는데, 위에서 작성했던 ex09.txt 파일을 변경하도록 해보자. 결과는 아래와 같고, 하이라이트 된 라인은 실제 프롬프트에서 명령을 입력한 라인이다.

c:\Apps\textfiles>c:\Python27\python ex10.py ex09.txt
We're going to erase 'ex09.txt'.
If you don't want that,hit Ctrl-C(^C).
If you do want that, hit RETURN.
?
Opening the file...
Truncating the file. Goodbye!
Now I'm going to ask you for three lines.
Line 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ
Line 2:abcdefghijklmnopqrstuvwxyz
Line 3:0123456789
I'm going to write these to the file.

c:\Apps\textfiles>type ex09.txt
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789

텍스트 파일의 이름을 파라미터로 주고 프로그램을 실행하면 먼저 기존 파일의 삭제 여부를 묻게 된다. 엔터를 누르면 계속 진행을 하게 되고 파일을 지운다. 그런 다음 세 줄의 텍스트를 입력 받고, 이를 파일에 기록하게 된다.type 명령 (터미널에서는 cat 명령)으로 파일의 내용을 살펴보면 입력한 내용으로 해당 파일의 내용이 바뀌어 있음을 볼 수 있다.

코드의 내용을 분석해보도록 하자.

1) 파일의 이름을 filename 이라는 변수에 대입해준다. 이 파일 이름은 파라미터로 전달받은 값이다.

2) 5~7 행에서는 파일의 이름을 표시하고, 취소할 기회를 준다. 이때 ^C (ctrl+c)를 누르면 프로그램의 실행이 취소되고, 엔터를 누르면 다음으로 진행하게 된다.

3) 11~15행에서 해당 파일을 “쓰기 모드”로 열고 내용을 모두 지운다. 파일핸들러의 truncate() 명령은 내용을 모두 지우는 기능을 수행한다. 사실 “w”를 사용해 쓰기 모드로 파일을 열고 다른 내용을 기록하면 이전의 내용은 모두 지워지게 된다. 따라서 15행은 실질적으로는 필요 없는 셈이다. (15행을 주석 처리하고 한 번 실행해보라.)

4) 17~21행에서 다시 각 라인을 입력 받아 line1, line2, line3에 각각 입력 받은 내용을 추가한다.

5) 25~30 행에서는 파일 핸들러 targetwrite() 기능을 이용해서 각각의 문자열을 파일에 쓴다. 이 때 각 line1, line2, line3 은 줄바꿈 문자를 포함하고 있지 않으므로 이를 추가해 준다. 물론 이는 target.write(line1+"\n") 과 같이 써서 줄바꿈 문자를 더한 문자열을 파일에 써도 된다.

6) 끝으로 파일을 닫는다.

물론 하나의 프로그램에서 파일을 2개 이상 동시에 여는 것도 가능하다. 각각의 열린 파일은 핸들러로 조작할 수 있으므로 2개의 핸들러를 open 명령으로 만들어주면 된다.

파일 복사 프로그램

이 글의 서두에서 언급한 파일 복사하는 프로그램을 작성해 보겠다. 사실은 무척이나 단순하다. 이 프로그램은 1) 파라미터로 원본 파일과 복사할 위치를 입력받는다. 2) 원본 파일의 길이(몇 바이트나 되는지)를 알려주고, 3)복사할 위치에 이미 파일이 존재하는지를 알려준다. 그런 다음 4) 원본 파일의 내용을 읽어와서 사본 파일에 쓴다. 5) 두 파일을 닫는다.

ex11.py

이상의 내용을 코딩한 ex11.py의 내용은 다음과 같다.

#-*-coding:utf-8
from sys import argv
from os.path import exists

script, from_file, to_file = argv

print "Copying from %s to %s" % (from_file, to_file)

    input = open(from_file)
indata = input.read()
    print "The input file is %d bytes long." % len(indata)

    print "Does the output file exists? %r" % exists(to_file)
    print "Reday, hit RETURN to continue, CTRL-C to abort."
raw_input()

    output = open(to_file, 'w')
output.write(indata)

    print "Alright, all done."

    output.close()
input.close()

실행 결과는 다음과 같다. 이미 만들어져있는 그리고 만만한 ex09.txt를 다른 파일로 복사해보도록 하겠다.

c:\Apps\textfiles>c:\Python27\python.exe ex11.py ex09.txt ex09.bak
Copying from ex09.txt to ex09.bak
The input file is 65 bytes long.
Does the output file exists? False
Reday, hit RETURN to continue, CTRL-C to abort.

Alright, all done.

c:\Apps\textfiles>type ex09.bak
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789

코드 내용 분석을 보자. 무척이나 단순하고 간단한데, 못 보던 기능을 발견했다. 바로 os.pathexists 라는 명령이다.

1) os.path 로부터 exists라는 함수를 반입해온다. os 는 파이썬 프로그램이 실행되는 컴퓨터의 운영체제와 관련된 모듈들의 집합이다. 그리고 그 중에는 path라는 모듈이 있는데, 이 모듈은 디스크 내의 경로, 파일 등과 관련된 기능들이 모여있다. exists 모듈은 path에 들어있는 하위 객체인데, 주어진 경로에 파일이 이미 존재하는지를 검사하여 존재한다면 True를 돌려주는 기능을 한다.

2) input 은 원본 파일을 열어서 그 핸들러를 얻는다. input의 read() 명령을 통해 파일의 전체 내용을 읽어 indata에 대입한다.

3) 13~15는 파일의 길이를 알려주고, 복사본 위치를 찾아 거기에 이미 파일이 있는지를 알려준다. 엔터를 입력하면 raw_input() 뒤의 코드가 계속 실행된다.

4) 'w' 모드로 복사본 파일을 연다. 그런 다음, indata를 쓴다.

5) 끝으로 두 파일을 닫으면 된다.

위의 실행 결과에서 알수 있듯이, 파라미터로 준 파일을 보면 내용이 복사되어 있음을 알 수 있다.

사실 파일 입출력은 쉬운 내용은 아니다. ‘핸들러’라는 눈에 보이지도 않는 추상적인 개념을 도구로 다루기 때문이다. 하지만 이런 추상적인 개념은 사실 이해하기보다는 익숙해지면 쉽게 느끼게 된다. (우리가 흔히 하는 덧셈, 뺄셈도 분명 그 원리는 우리가 알고 있지만 엄밀하게는 익숙하게 느끼기 때문에 쉽게 생각할 수 있는 것이다.)

파일 입출력에 대해 간단히 알아보았다. 이로써 기본적인 입력과 출력을 처리할 수 있는 방법에 대해서는 기본기를 파악했으니, 다음 시간부터 실제 프로그램을 움직이는 흐름인 로직에 대해 살펴보기로 하겠다.