5장. 입출력

프로그램이 사용자로부터 입력을 받아들이지 않는다면 그다지 쓸모가 없을 것이다. 마찬가지로, 출력값을 위한 양식을 제공하지 않는 프로그램이 만약 존재한다면 사람들은 그런 것이 왜 필요한지 의아해 할 것이다. 어떤 프로그램에서든 입력 및 출력 작업이 사용자의 경험과 유용성을 좌우한다. 이 장에서는 프로그램으로 정보 또는 자료를 넣는 방법, 그리고 정보 또는 자료를 표시하거나 파일에 저장하는 방법을 다룬다. 여기서는 데이터베이스를 다루지는 않을 것이며, 그보다 한층 낮은 수준이라 할 수 있는 파일에 대하여 논의할 것이다. 이번 장을 통하여 여러분은 터미널이나 명령행을 통해 프로그램에 자료를 입력하는 것과 같은 기법을 배울 것이다. 또한, 파일로부터 입력값을 읽거나 파일에 기록하는 방법도 배울 수 있다. 피클 모듈을 사용하여 디스크에 파이썬 개체를 지속시키는 방법, 또한 디스크에서 개체를 검색하고 이를 사용하는 방법도 알게 될 것이다.

키보드로부터 입력 받기

위에서 말한 바와 같이, 거의 모든 프로그램은 한 형태 또는 다른 형태에서 사용자로부터 입력을 취한다. 대부분의 기본적인 어플리케이션에서는 터미널이나 명령행 환경을 거쳐서 키보드 입력을 할 수 있다. 키보드로 입력을 가능하게 하는 수많은 방법을 가진 파이썬의 많은 다른 기술이 있기에, 파이썬은 키보드 입력이 쉽다. 이 절에서, 이러한 각각의 방법에 대하여 그 원리와 용례를 살펴보도록 하자. 여러분의 필요에 따라 입출력을 수행하는 가장 적합한 방법을 선택할 수 있게 될 것이다.

sys.stdin 와 raw_input

sys.stdin 은 명령행이나 터미널로부터의 입력을 읽기 위한 방법으로 널리 사용된다. 이 절차는 sys 패키지를 가져와서, 사용자에게 입력을 요구하고, 마지막으로 sys.stdin.readln()을 호출하여 입력을 읽어서 반환된 값을 변수에 할당하도록 구성되었다. 그 과정을 예제 5-1의 코드로 나타내었다.

예제 5-1. sys.stdin 사용하기

# 명령행에서 값을 얻어 변수에 저장
>>> import sys
>>> fav_team = sys.stdin.readline()
Cubs
>>> sys.stdout.write("My favorite team is: %s" % fav_team)
My favorite team is: Cubs

위와 같이 sys 모듈은 매우 사용하기 쉽다. 동일한 작업을 하는 다른 방법으로는 raw_input 함수를 사용할 수 있다. 이 함수는 동일한 절차를 수행하는 데 있어 보다 단순한 구문을 사용한다. raw_input은 기본적으로 명령행 또는 터미널에서 어떤 텍스트를 생성하고, 사용자 입력을 허용하고, 변수에 그것을 할당한다. 이제 raw_input 구문을 사용하여 위에서 같은 예를 살펴 보자. 유사한 작업을 수행하는 다른 함수로서 input이라는 이름을 가진 것도 있다. 그러나, input 함수는 잠재적인 보안 위험을 안고 있으므로 아주 조심스럽게 사용해야 한다. 입력 함수가 콘텐츠를 반환하고 표현식으로 그것을 평가하는 반면에 raw_input 함수는 항상 문자열로 전달 내용을 반환한다. 될 수 있으면 input 함수는 사용하지 않는 것이 상책이다.

예제 5-2. raw_input 사용하기

# raw_input를 사용하여 값을 얻고, 그것은 변수로 저장
>>> fav_team = raw_input("Enter your favorite team: ")
Enter your favorite team: Cubs

Jython 환경에서 변수 얻기

응용 프로그램 내에서 사용하기 위해 자이썬 환경에서 직접 값을 검색할 수도 있다. 예를 들어, 우리는 시스템 환경 변수 나 프로그램을 실행할 때 명령행 또는 터미널로 전달되는 문자열을 얻을 수 있다.

자이썬 어플리케이션 안에서 환경변수를 사용하기 위해서는, 간단하게 os모듈을 임포트하고, environ 사전을 사용한다. 이것은 사전 객체이기 때문에, 당신은 단순히 os.environ을 입력하여 모든 환경 변수의 목록을 얻을 수 있다.

예제 5-3. 시스템 환경 변수를 얻고 변경하기

>>> import os
>>> os.environ["HOME"]
'/Users/juneau'
# 파이썬 세션을 위한 홈 디렉토리 바꾸기
>>> os.environ["HOME"] = "/newhome"
>>> os.environ["HOME"]
'/newhome'

명령 프롬프트 또는 터미널에서 자이썬 모듈을 실행하는 경우에는, 자이썬 모듈 호출 후 명령행 또는 터미널으로부터 얻은 값의 목록인 sys.argv를 사용할 수 있다. 예를 들어, 만약 우리의 프로그램 사용자가 모듈에서 사용되는 몇가지 인수를 입력하는데 관심이 있다면, 사용자가 간단하게 모듈을 호출하고, 공백이 따라붙은 텍스트 입력을 쳐넣을 수 있으며, 만약 공백을 포함한 인자를 전달하고 싶으면 따옴표를 사용한다. 인수의 갯수는 몇 개가 되든 괜찮다(갯수 제한에 걸린 적은 없었다). 가능성은 무한하다.

예제 5-4. sys.argv 사용하기

# sysargv_print.py – 명령줄에서 제공한 인수를 모두 출력
import sys
for sysargs in sys.argv:
    print sysargs
# 사용법
>>> jython sysargv_print.py test test2 "test three"
sysargv_print.py
test
test2
test three

예제에서 보는 바와 같이, sys.argv의 첫 번째 항목은 스크립트의 이름이며, 다음 모듈 이름 뒤에 제공되는 각각의 추가 인수는 다음 sys.argv 목록에 추가된다. 이것은 태스크 자동화 등에 사용할 스크립트를 만드는 데 매우 유용하다.

파일 입출력

우리는 2장에서 파일 데이터 형식에 대해 조금 배웠다. 그곳에서, 이 유형을 사용하여 수행할 수 있는 연산 몇 가지에 대하여 간략히 논의하였다. 이번 장에서는 파일 개체를 가지고 할 수 있는 것에 대하여 자세히 살펴보려고 한다. 우선 기초적인 부분에서 시작하여, 보다 깊이 들어가도록 하겠다. 우선, 파일 개체에 대하여 사용 가능한 메소드에는 어떤 것이 있고, 각각은 무엇을 하는지 표 5-1에서 살펴보기 바란다.

표 5-1. File 개체 메소드

메소드 설명
close() 파일을 닫음
fileno() 정수 파일 기술자(descriptor)를 반환
flush() 출력 버퍼를 밀어버리거나 내용을 파일에 쓰고서 지움
isatty() 파일이 대화식 터미널인 경우 1을 반환
next() 파일에 대하여 반복(iterate)하도록 한다. 파일의 다음 행을 반환 한다. 반환할 행이 없으면 StopIteration을 일으킨다.
read(x) x 바이트만큼 읽음
readline(x) 한 행에 대하여 최대 x개까지의 문자를 읽음. 또는 x가 생략된 경 우에는 전체 행을 읽음.
readlines(size) 파일의 모든 행을 읽어 목록에 넣음. size > 0인 경우, 그 수만큼 의 문자를 읽음.
seek() 커서를 파일의 새로운 위치로 이동
tell() 커서의 현재 위치를 반환
truncate(size) 파일의 size 만큼을 버린다. 특정하지 않았을 경우 size의 기본값 은 현재 위치가 된다.
write(string) 파일 개체에 string을 기록
writelines(seq) 순서형 자료에 포함된 모든 문자열을 구분자 없이 기록

사용할 파일을 만드는 것부터 해보자. 2장에서 논의한 바와 같이, 내장 함수인 open(filename[, mode])은 파일을 생성하며 그것을 특정한 방법으로 연다. mode는 파일을 열 때 읽기 전용인지, 혹은 읽고 쓰기가 가능한지와 같은 모드를 지정하는 부분이다.

예제 5-5. 파일의 생성, 열기 및 쓰기

>>> my_file = open('mynewfile.txt','w')
>>> first_string = "This is the first line of text."
>>> my_file.write(first_string)
>>> my_file.close()

예제에서, 파일 “mynewfile.txt”는 open 함수가 호출되기 전까지는 존재하지 않는다. 만약에 이미 존재했다면, 이전의 내용은 새로운 내용으로 덮어쓰게 되므로 파일은 비게 된다. 파일은 쓰기 모드로 생성되었으며, 파일에 문자열을 기록한다. 파일을 닫거나 flush()를 수행하기 전까지는 first_thing은 실제로는 파일에 기록되지 않는다는 것을 언급하지 않을 수 없다. 만약 우리가 파일을 닫고, 다시 열고, 파일에 후속 write() 연산을 수행했다면, 파일의 이전 내용은 새롭게 쓴 내용으로 덮어써지게 된다.

이제 우리는 예제에서 파일 함수의 각 절차를 밟아볼 것이다. 이 예제는 여러분이 실제로 작동하는 파일 입출력 코드를 볼 수 있도록 하는 데에 초점을 맞추었다.

예제 5-6.

# 파일에 행을 쓰고 flush하고 닫기
>>> my_file = open('mynewfile.txt','w')
>>> my_file.write('This is the first line of text.\n')
>>> my_file.write('This is the second line of text.\n')
>>> my_file.write('This is the last line of text.\n')
# 선택사항으로서, 파일을 닫을 것이라면 전혀 불필요하지만, 버퍼를 정리하는 데에는 유용하다.
>>> my_file.flush()
>>> my_file.close()
# 읽기모드로 파일열기
>>> my_file = open('mynewfile.txt','r')
>>> my_file.read()
'This is the first line of text.\nThis is the second line of text.\nThis is the last line of text.\n'
# 만일 다시 읽으면, 커서가 파일의 끝에 있기 때문에 결과는 ''이다.
>>> my_file.read()
''
# 파일의 시작점을 다시 찾아서 읽기
>>> my_file.seek(0)
>>> my_file.read()
'This is the first line of text.This is the second line of text.This is the last line of text.'
# 파일의 시작점을 다시 찾아서 readline() 수행
>>> my_file.seek(0)
>>> my_file.readline()
'This is the first line of text.\n'
>>> my_file.readline()
'This is the second line of text.\n'
>>> my_file.readline()
'This is the last line of text.\n'
>>> my_file.readline()
''
# 현재 커서 위치를 표시하기위해 tell() 사용하기
>>> my_file.tell()
93L
>>> my_file.seek(0)
>>> my_file.tell()
0L
# 파일의 행에 대하여 되풀이
>>> for line in my_file:
...     print line
...
This is the first line of text.
This is the second line of text.
This is the last line of text.

우리가 파일 개체에 관한 더 많은 정보를 찾기 위해 사용할 수있는 소수의 읽기 전용 속성이 있다. 예를 들어, 만약 우리가 파일을 가지고 작업하는 중에 파일이 열린 상태로 있는지, 닫힌 상태인지 알고 싶다면, 우리는 파일이 닫혀 있는지 여부를 나타내는 부울을 반환하는 파일에 대한 closed 속성을 확인하면 된다. 표 5-2는 파일 개체에 관해 알려주는 것과 그것들의 각각의 속성들의 목록이다.

표 5-2. 파일 속성

예제 5-7. 파일 속성 사용

>>> my_file.closed
False
>>> my_file.mode
'r'
>>> my_file.name
'mynewfile.txt'

피클

파이썬 언어에서 가장 인기있는 모듈 중 하나는 피클(pickle) 모듈이다. 이 모듈의 기본적인 목적은 상하기 쉬운 오이를 절여서 오이지를 만들듯이 휘발성의 파이썬 개체를 직렬화하여 파일의 형태로 디스크에 지속시키는 것이다. 절여진 개체는 pickle 모듈을 통하여 디스크에 기록할 수 있으며, 그것을 다시 읽어들여서 개체의 형태로 사용할 수 있다. 피클은 어떠한 파이썬 개체에든지 적용할 수 있다.

체를 디스크에 기록할 때에는 pickle() 함수를 호출한다. 그 개체는 다른 방법으로는 읽을 수 없는 형식으로 디스크에 기록되지만, 우리는 그 파일을 프로그램으로 읽어들여서 개체가 파일에 쓰여지기 전의 모습 그대로 사용할 수 있다. 다음은 선수(Player) 개체를 만든 다음 그것을 pickle을 사용하여 파일에 지속시키는 예제이다. 나중에는 그것을 프로그램으로 읽어들여서 사용하게 된다. pickle 모듈을 가지고 작업할 때에는 파일 개체를 사용한다.

예제 5-8. pickle 모듈을 사용하여 개체를 디스크에 기록

>>> import pickle
>>> class Player(object):
...     def __init__(self, first, last, position):
...         self.first = first
...         self.last = last
...         self.position = position
...
>>> player = Player('Josh','Juneau','Forward')
>>> pickle_file = open('myPlayer','wb')
>>> pickle.dump(player, pickle_file)
>>> pickle_file.close()

위의 예제에서는, pickle 모듈의 dump(개체, 파일) 메소드를 사용하여 Player 개체를 디스크에 담궈두었다. 이제 그 개체를 우리의 프로그램으로 다시 읽어들여서 출력해보도록 하자.

예제 5-9. pickle 개체를 읽어서 사용하기

>>> pickle_file = open('myPlayer','rb')
>>> player1 = pickle.load(pickle_file)
>>> pickle_file.close()
>>> player1.first
'Josh'
>>> player1.last, player1.position
('Juneau', 'Forward')

마찬가지로, 위에서는 피클된 파일을 load(파일) 메소드를 사용하여 다시 읽어들였다. 일단 읽어서 변수에 저장해놓으면, 파일은 닫아두고 개체를 가지고 작업할 수 있다. 만약에 dump나 load 작업을 순서에 입각해서 처리할 필요가 있다면, 차례차례 처리하는 데에도 문제가 없다. 파이썬 환경에 따라 피클을 구현한 규약에 차이가 있으니 주의하기 바란다. 기본 규약은 0이지만 1과 2도 사용 가능하다. 기본 규약을 따르는 것이 대부분의 환경에서 잘 작동하므로 안전하지만, 만약에 이진 포맷과 관련한 문제를 겪게 된다면 그때는 다른 것들도 시도해보기 바란다.

개체를 디스크에 저장해두었다가 나중에 참조할 필요가 있다면, 피클된 개체에 대한 사전과 같은 역할을 하는 shelve 모듈을 사용하면 좋다. shelve 기법에 대해서 간단히 설명하자면, 개체를 피클해서 문자열 기반의 키 값으로 저장한다고 할 수 있다. 그 개체는 나중에 파일을 열고 키를 전달하여 얻어낼 수 있다. 이러한 기법은 마치 키 값으로 언제든지 참조할 수 있는 개체가 들어있는 서류철과 같다고 하겠다. 이러한 기법이 어떻게 동작하는지 살펴보도록 하자.

예제 5-10. Shelve 기법 사용하기

# 각기 다른 Player 개체들을 저장
>>> import shelve
>>> player1 = Player('Josh','Juneau','forward')
>>> player2 = Player('Jim','Baker','defense')
>>> player3 = Player('Frank','Wierzbicki','forward')
>>> player4 = Player('Leo','Soto','defense')
>>> player5 = Player('Vic','Ng','center')
>>> data = shelve.open("players")
>>> data['player1'] = player1
>>> data['player2'] = player2
>>> data['player3'] = player3
>>> data['player4'] = player4
>>> data['player5'] = player5
>>> player_temp = data['player3']
>>> player_temp.first, player_temp.last, player_temp.position
('Frank', 'Wierzbicki', 'forward')
>>> data.close()

위의 시나리오에서, 우리는 이전 예제에 정의된 것과 동일한 Player 개체를 사용한다. 우리는 새 shelve를 열고, “players”라고 이름지었다. 이 shelve는 디스크에 씌여지는 세 파일의 집합으로 구성되어있다. 이 세 파일들은 “players.bak”, “players.dat”, “players.dir”란 이름으로 디스크에서 찾을 수 있다. 그리고 개체가 close()를 호출할 때 각 개체들은 shelve로 저장된다. 우리가 인스턴스화한 Player 개체들은 모두 동일한 shelve로 저장되지만, 이들에는 각기 키가 부여된다. 개체들이 고유한 한은 우리가 원하는 키들을 이름지을 수 있다. 예제에서, 우리는 다섯 개의 개체를 저장하고, 끝에 가서는 개체들 중 하나를 꺼내어서 출력했다. 이것은 작은 데이터 저장소를 만드는 데에 있어 상당히 좋은 기법이다.

출력 기법

2장에서 문자열 형식을 논할 때에 print 문에 대한 기본적인 내용을 다루었다. print 문은 현재까지 대부분의 파이썬 프로그램에서 가장 널리 사용되는 출력 형태이다. 2장에서 형 변환이나 출력 행의 서식을 지정하는 방법과 같은 기본적인 내용을 다루었는데, 여기서는 출력을 생성하는 다른 기법들과 함께 print 문의 몇 가지 다른 사용법에 대하여 좀 더 깊이 들어가보도록 하겠다. print문에서 사용할 수 있는 기본적인 형식에는 두 가지가 있다. 첫 번째로 2장에서 다룬 것은, 문자열과 변환 형식 앞에 퍼센트 (%) 기호를 붙인 문자열을 사용하는 것이다. 그 문자열 뒤에서, 또 다른 퍼센트(%) 기호와 그 뒤에 괄호 쳐진 인자 목록을 사용하여 문자열 내의 변환 유형을 순서대로 바꿔치기한다. 아래에서 각각의 예를 확인해보자.

예제 5-11. print 문을 사용한 출력

# % 기호 사용
>>> x = 5
>>> y = 10
>>> print 'The sum of %d and %d is %d' % (x, y, (x + y))
The sum of 5 and 10 is 15
>>> adjective = "awesome"
>>> print 'Jython programming is %s' % (adjective)
Jython programming is awesome

또한 문자열에 대장된 변환 형을 사용하여 부동소수 형식을 출력할 수도 있다. 내재된 변환 유형에 ”.#” 구문을 사용함으로써 숫자값을 특정할 수 있다.

예제 5-12. 부동소수 연산 결과에 대한 서식 지정

>>> pi = 3.14
>>> print 'Here is some formatted floating point arithmetic: %.2f' % (pi + y)
Here is some formatted floating point arithmetic: 13.14
>>> print 'Here is some formatted floating point arithmetic: %.3f' % (pi + y)
Here is some formatted floating point arithmetic: 13.140

요약

파이썬에는 당연히 자체적인 입출력 전략이 있다. 이번 장에서는 기본적인 터미널 또는 명령행 입출력에서 시작하여 파일 조작에 이르기까지, 이러한 기법의 대부분을 다루었다. open 함수를 사용하여 파일을 생성하고, 읽고 기록하는 방법을 배웠다. 명령행 sys.argv 인자는 입력을 얻는 또 다른 방법이며, 환경변수 또한 프로그램 내에서 사용할 수 있다. 이러한 주제 뒤에는, 피클 모듈을 간단히 살펴보고 그것이 파이썬 개체를 어떻게 디스크에 지속시키는지 알아보았다. shelve 모듈은 다중 개체에 대한 색인을 구성하고 동일 파일 내에 저장할 수 있게 해주는 또다른 방법이다. 끝으로, 우리는 프로그램에서 출력을 다루는 두 가지 기법을 논의했다.

입출력의 상세한 내용만으로 책 전체를 할애할 수도 있겠으나, 이 장에서는 파이썬의 입출력에 대한 광범위한 주제에 대한 견고한 출발점을 제공한다. 이 책에서 다루는 파이썬 언어의 여러 특징과 마찬가지로, 웹과 책의 형태로 많은 자료가 있으니 원한다면 더 찾아보기 바란다. Magnus Lie Hetland의 Beginning Python: From Novice to Professional을 추천한다. www.python.org/doc/에서 구할 수 있는 파이썬 문서 또한 읽어볼 만 하다.