부제목


7. 입력과 출력

여러가지 방법으로 프로그램의 출력을 나타낼 수 있습니다; 데이터는 사람이 읽을 수 있는 형태로 인쇄될 수 있습니다. 또는 미래에 사용하기 위해 파일로 쓸 수도 있습니다. 이 장에서는 그 방법들을 공부해 보겠습니다.


7.1 멋진 출력 형식화

지금까지 두 가지 방법으로 값을 써 보았습니다: 표현식 서술문print 서술문으로 말입니다. (세 번째 방법은 파일 객체의 write() 메쏘드를 사용하는 것입니다; 표준 출력 파일은 sys.stdout으로 참조가 가능합니다. 이에 관한 더 자세한 정보는 라이브러리 참조서를 보세요.)

단순히 공간문자로-분리된 값을 인쇄하는 것 말고 출력의 형식을 좀 더 섬세하게 통제하고 싶은 경우가 자주 있을 것입니다. 출력을 형식화하려면 두 가지 방법이 있습니다; 첫번째 방법은 문자열 처리를 직접 손수 처리하는 것입니다; 문자열 조각썰기와 결합 연산을 사용하면 상상한 대로 얼마든지 조감을 만들어 낼 수 있습니다. string 표준 모듈에는 주어진 컬럼 너비 만큼 문자열을 덧대는 유용한 연산들이 들어 있습니다; 이런 연산들은 잠시 후에 살펴보겠습니다. 두 번째 방법은 문자열과 함께 % 연산자를 왼쪽 인자로 사용하는 것입니다. % 연산자는 왼쪽의 인자를 마치 오른쪽 인자에 적용될 sprintf()-스타일의 포맷 문자열로 해석합니다. 그리고 이 형식화 연산의 결과로 나온 문자열을 돌려줍니다.

물론, 한 가지 문제가 남아 있습니다: 어떻게 값을 문자열로 변환할까요? 다행스럽게도, 파이썬은 어떤 값이든 문자열로 변환하는 방법을 제공합니다: 그 값을 repr()이나 str() 함수에 건네면 됩니다. 역따옴표(``)는 repr()과 동일하지만, 현재 파이썬 코드에서는 더 이상 사용되지 않으며 앞으로 파이썬에서 사용되지 않을 것입니다.

str() 함수의 의도는 인간이-읽을 수 있는 표현을 돌려주는 것입니다. 반면에 repr() 함수는 인터프리터가 읽을 수 있는 형태로 표현을 만들어내는 것이 의도입니다 (적당한 구문이 없다면 SyntaxError를 일으킬 것입니다). 인간이 읽기에 특별한 표현이 없는 객체라면, str()repr()과 같은 값을 돌려줍니다. 숫자나 리스트 또는 사전 같이 많은 값들이 두 함수 중의 하나를 사용하여 똑같이 표현됩니다. 문자열과 부동소수점수는 특히 두 가지 다른 표현이 있습니다.

몇가지 예를 들면:

>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(0.1)
'0.1'
>>> repr(0.1)
'0.10000000000000001'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ',  y is ' + repr(y) + '...'
>>> print s
The value of x is 32.5,  y is 40000...
>>> # 문자열의 repr()은 따옴표와 역사선이 추가된다:
... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print hellos
'hello, world\n'
>>> # repr()의 인자는 파이썬 객체면 무엇이든 된다:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
>>> # 역따옴표는 상호대화 세션에서 편리하다:
... `x, y, ('spam', 'eggs')`
"(32.5, 40000, ('spam', 'eggs'))"

다음에 두 가지 방식으로 평방면적과 입방체적을 써 보겠습니다:

>>> for x in range(1, 11):
...     print repr(x).rjust(2), repr(x*x).rjust(3),
...     # 앞 줄에서 뒤따르는 쉼표에 주의
...     print repr(x*x*x).rjust(4)
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

>>> for x in range(1,11):
...     print '%2d %3d %4d' % (x, x*x, x*x*x)
... 
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

(앞 예제에서 각 컬럼 사이에 공간문자 하나가 print가 작동하는 방식처럼 추가되었음을 주목하세요: 언제나 인자 사이에 공간문자가 추가됩니다.)

이 예제는 문자열 객체의 rjust() 메쏘드의 사용법을 보여줍니다. 이 메쏘드는 왼쪽에 공간문자를 덧대어 주어진 너비에서 문자열을 오른쪽으로 정렬합니다. 비슷하게 ljust()center() 메쏘드도 있습니다. 이 메쏘드들은 아무것도 쓰지 않고, 단지 새로운 문자열을 돌려줄 뿐입니다. 입력 문자열이 너무 길더라도, 잘라내지 않고, 그냥 그대로 돌려줍니다; 이 때문에 컬럼의 조감의 엉망이 되겠지만 그 편이 값에 관하여 거짓말을 하는 것보다는 보통 더 좋습니다. (정말 잘라내고 싶다면 언제든지 조각썰기 연산을 하면 됩니다. "x.ljust(n)[:n]"의 형태로 말입니다.)

또다른 메쏘드로 zfill()이 있는데, 이 메쏘드는 숫치 문자열의 왼쪽을 0으로 채워줍니다. 또 덧셈 기호와 뺄셈 기호를 이해합니다:

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

% 연산자를 사용하면 다음과 같이 보입니다:

>>> import math
>>> print 'The value of PI is approximately %5.3f.' % math.pi
The value of PI is approximately 3.142.

문자열에 포맷이 여럿이라면, 다음과 같이 터플을 오른쪽 피연산자로 건네면 됩니다:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
...     print '%-10s ==> %10d' % (name, phone)
... 
Jack       ==>       4098
Dcab       ==>       7678
Sjoerd     ==>       4127

대부분의 포맷은 C에서처럼 작동하고 적절한 유형을 건네 주어야 합니다; 그렇지만, 적절하게 유형을 건네지 않더라도 코어 덤프가 아니라 예외를 맞이합니다. %s 형식이 더 편합니다: 상응하는 인자가 문자열 객체가 아니면, str() 내장 함수를 사용하여 문자열로 변환됩니다. *를 사용하면 너비나 정밀도를 별도의 (정수) 인자로 건넬 수 있습니다. C 포맷의 %n%p는 지원되지 않습니다.

가르고 싶지 않은 엄청나게 긴 포맷 문자열이 있다면, 변수를 참조하여 위치 대신에 그 이름으로 포맷을 할 수 있으면 좋을 것입니다. 이것은 아래와 보여주는 바와 같이 %(name)format 형태를 사용하면 됩니다:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

이는 새로 내장된 vars() 함수와 결합해 사용하면 특히 유용합니다. 이 함수는 지역 변수를 모두 담고 있는 사전을 돌려줍니다.


7.2 파일 읽기와 쓰기

open() 함수는 파일 객체를 돌려줍니다. 그리고 보통 두 가지 인자와 함께 많이 사용됩니다: "open(filename, mode)".

>>> f=open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

첫 인자는 문자열로서 파일이름을 담고 있습니다. 두 번째 인자는 또다른 문자열로서 파일이 사용될 방식을 기술하는 몇 가지 문자를 담고 있습니다. mode는 파일이 읽기 전용이라면 'r'이 되고, 쓰기 전용이라면 'w'가 되며 (기존의 파일이 같은 이름을 가지고 있다면 삭제됩니다), 그리고 추가를 위한 것이라면 'a'가 됩니다; 파일에 씌여지는 데이터는 모두 자동으로 끝에 추가됩니다. 'r+'는 읽기와 쓰기 모드로 파일을 엽니다. mode 인자는 선택적입니다; 생략되면 'r'로 간주됩니다.

윈도우즈와 매킨토시에서, 모드 뒤에 'b'가 더 붙으면 파일을 이진 모드로 엽니다. 그래서, 'rb', 'wb', 그리고 'r+b'와 같은 모드도 있습니다. 윈도우즈는 텍스트와 이진 파일을 구분합니다; 텍스트 파일에서 줄-끝 문자는 데이터가 읽혀지거나 씌여지면 자동으로 약간 바뀝니다. 이렇게 뒤에서 파일 데이터를 변조하는 것은 ASCII 텍스트 파일에는 문제가 없지만, JPEG이나 EXE같은 이진 파일은 부패시켜 버릴 것입니다. 그런 파일을 읽고 쓸때는 이진 모드를 사용하도록 주의하세요.


7.2.1 파일 객체의 메쏘드

이 섹션에서 예제들은 f라는 파일 객체가 이미 만들어져 있다고 가정하겠습니다.

파일의 내용을 읽으려면, f.read(size)를 호출하면 되는데, 데이터의 일부를 읽어서 그것을 문자열로 돌려줍니다. size는 선택적인 숫치 인자입니다. size가 생략되거나 음수이면, 파일의 전체 내용을 읽어서 돌려줍니다; 파일이 메모리보다 두 배가 된다면 그것은 전적으로 여러분의 문제입니다. 그렇지 않으면 기껏해야 size 바이트만큼만 읽어서 돌려줍니다. 파일의 끝에 도달하면, f.read()는 빈 문자열("")을 돌려줍니다.

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline()은 파일에서 한 줄을 읽습니다; 새줄문자(\n)는 문자열의 끝에 남겨둡니다. 그리고 그 파일이 새줄문자로 끝나지 않는다면 파일의 마지막 줄에서만 생략됩니다. 이 때문에 반환 값이 명확해 집니다; f.readline()이 빈 문자열을 돌려주면, 파일의 끝에 도달한 것이고, 반면에 빈 줄은 '\n'로, 즉 새줄문자 한개를 담고 있는 문자열로 표현됩니다.

>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''

f.readlines()이 돌려주는 리스트는 그 파일의 모든 줄을 담고 있습니다. 선택적인 매개변수 sizehint가 주어지면, 파일에서 한 줄을 구성할 만큼 충분히 바이트를 읽어서 그로부터 만든 줄들을 돌려줍니다. 이는 거대한 파일을 줄 단위로 효율적으로 읽는데 사용됩니다. 그러나 전체 파일을 메모리로 적재할 필요가 없습니다. 완료된 줄들만 돌려줍니다.

>>> f.readlines()
['This is the first line of the file.\n', 'Second line of the file\n']

줄을 읽는 다른 방법은 파일 객체를 순회하는 것입니다. 메모리에 효율적이고 빠르며 더 코드가 단순해 집니다:

>>> for line in f:
        print line,
        
This is the first line of the file.
Second line of the file

이 방법이 더 간단하지만 섬세하게 통제하지 못합니다. 두 가지 방법이 라인 버퍼링을 서로 다르게 관리하기 때문에, 둘은 서로 섞어쓰지 못합니다.

f.write(string)string의 내용물을 파일에 쓰고, None을 돌려줍니다.

>>> f.write('This is a test\n')

문자열 말고 다른 것을 쓰려면, 먼저 문자열로 변환되어야 합니다:

>>> value = ('the answer', 42)
>>> s = str(value)
>>> f.write(s)

f.tell()은 파일의 처음에서부터 바이트로 측정하여 파일 객체의 현재 위치를 나타내는 정수를 돌려줍니다. 파일 객체의 위치를 바꾸려면, "f.seek(offset, from_what)"을 사용하면 됩니다. 위치는 참조 위치에 offset 을 더해서 계산됩니다; 참조 위치는 from_what 인자로 선택됩니다. from_what 값이 0이면 파일의 처음에서부터 측정되고, 1이면 현재 파일 위치를 사용하며, 2이면 파일 끝을 참조 위치로 사용합니다. from_what은 생략될 수 있고 기본 값은 0입니다. 즉 파일의 처음을 참조 위치로 사용합니다.

>>> f = open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # 파일의 6번째 바이트로 간다
>>> f.read(1)        
'5'
>>> f.seek(-3, 2) # 끝에서 3번째 바이트로 간다
>>> f.read(1)
'd'

파일 작업이 끝나면, f.close()를 호출하여 파일을 닫고 열린 파일이 차지했던 시스템 자원을 풀어줍니다. f.close()를 호출하고 난 후에, 그 파일 객체를 사용하려고 하면 자동으로 실패합니다.

>>> f.close()
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file

파일 객체에는 몇 가지 메쏘드가 더 있습니다. 예를 들어 isatty()truncate()는 별로 사용되지 않습니다; 파일 객체에 관한 완벽한 가이드는 라이브러리 참조서를 열어보세요.


7.2.2 pickle 모듈

문자열은 쉽게 파일로 읽고 쓸 수 있습니다. 숫자는 좀 더 노력이 필요합니다. 왜냐하면 read() 메쏘드는 문자열만 돌려주기 때문인데, 이 문자열은 int() 같은 함수에 건넬 필요가 있습니다. 이 함수는 '123'같은 문자열을 취해서 그의 숫치 값 123을 돌려줍니다. 그렇지만, 좀 더 복잡한 데이터 유형을 저장하고 싶다면, 예를 들어 리스트나 사전 또는 클래스 변수 같은 것들을 저장하려면 상황은 훨씬 더 복잡해 집니다.

사용자가 끊임 없이 코드를 쓰고 디버깅하면서 복잡한 데이터 유형을 저장하도록 하는 대신에, 파이썬은 표준 모듈에 pickle이라는 모듈을 제공합니다. 이 모듈은 거의 모든 파이썬 객체를 취해서 (심지어 파이썬 코드 형태로도!), 그것을 문자열 표현으로 변환하는 놀라운 모듈입니다; 이 과정을 이른바 절이기(pickling)라고 합니다. 문자열 표현으로부터 객체를 재구성하는 것을 절이기환원(unpickling)이라고 부릅니다. 절이기와 절이기환원 을 통해, 객체를 나타내는 문자열 표현은 파일이나 데이터에 저장도 되고, 네트워크 접속을 타고 먼 머신까지 전송되기도 합니다.

x라는 객체가 있고, 쓰기용으로 열린 f라는 파일 객체가 있다면 가장 손쉽게 그 객체를 절이는 방법은 코드 한 줄이면 됩니다:

pickle.dump(x, f)

f가 읽기용으로 연 파일 객체이고, 그 객체를 다시 절임환원하려면:

x = pickle.load(f)

(다른 방식도 있습니다. 많은 객체를 절이거나 또는 절여진 데이터를 파일로 쓰고 싶지 않거나 할 때 사용됩니다; 완전한 문서는 파이썬 라이브러리 참조서pickle 섹션을 참조하세요.)

절이기(pickle)는 다른 프로그램에 저장되거나 재사용 가능한 또는 같은 프로그램에서 앞으로 요청에 재사용될 파이썬 객체를 만드는 표준적 방법입니다; 이에 대한 기술적 용어는 영속(persistent) 객체입니다. pickle은 아주 널리 사용되기 때문에, 파이썬 확장을 작성하는 저자들은 행렬같은 새로운 데이터 유형이 제대로 절여지고 절임환원될 수 있도록 주의해야 합니다.

변경 제안에 관한 정보는 이 문서에 관하여를 참조.