You are here: Home Dive Into Python 3

Difficulty level: ♦♢♢♢♢

처음 만나는 파이썬 프로그램

Don’t bury your burden in saintly silence. You have a problem? Great. Rejoice, dive in, and investigate.
당신의 짐을 고요한 침묵속에 묻어두지 마라. 문제가 있는가? 좋다. 기뻐하고, 뛰어들어, 탐구하자!
Ven. Henepola Gunaratana

 

Diving In

여기서 파이썬 규약을 일일히 설명하려면 기본적인 요소들을 나열해 지루하게 만들 수 밖에 없고, 그런 방식으로는 나중에야 점차 유용한 무언가를 만들 수 있을겁니다. 그런것들은 다 넘어갑시다. 여기 완전하고 실행되는 파이썬 프로그램이 있습니다. 이 코드의 내용이 아직 전혀 이해되지 않을지도 모릅니다. 앞으로 한 줄씩 상세하게 분석하게 될테니 걱정하지 마세요. 먼저 이 코드를 쭉 읽어 본 후, 할 수 있다면 그 의미에 대해 생각해봅시다.

[download humansize.py]

SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    '''Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

    '''
    if size < 0:
        raise ValueError('number must be non-negative')

    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:
        size /= multiple
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)

    raise ValueError('number too large')

if __name__ == '__main__':
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

이제 이 프로그램을 커맨드라인에서 실행해 봅시다. 윈도우즈에서는 이런식으로 쓰세요:

c:\home\diveintopython3\examples> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

맥 OS X나 Linux에서는 , 이렇게 쓰세요:

you@localhost:~/diveintopython3/examples$ python3 humansize.py
1.0 TB
931.3 GiB

무슨 일이 일어났나요? 당신은 지금 파이썬 프로그램을 처음으로 실행 시켰습니다. 커맨드라인에서 파이썬 실행기를 불러와, 실행할 파이썬 스크립트의 이름을 넘겨줍니다. 그 스크립트에는 하나의 함수가 정의되어 있습니다. 바이트 단위의 정확한 파일 크기를 (근사치의) 규격 크기로 변환해주는 approximate_size()라는 함수입니다. (아마도 윈도우 익스플로러나 맥 오에스 X의 파인더, 아니면 리눅스의 노틸러스나 돌핀, 써너등에서 보았을 겁니다. 문서 폴더에서 자세히 보기를 선택하면 문서의 아이콘, 이름, 크기, 형태, 최근 수정일 등이 나타나게 되죠. 만약 폴더에 TODO라는 1093바이트 크기의 파일이 있다면 파일 매니저가 TODO 1093 bytes 라고 나타내는 대신 TODO 1 KB 라고 나타냅니다. 이게 approximate_size() 함수가 하는 일입니다.)

스크립트 끝 부분 보면 print(approximate_size(넘김값))를을 두 번 부르는 것을 볼수 있습니다. 이를 함수부름이라 합니다 — 처음에 approximate_size() 함수를 불러 몇개의 넘김값을 주고 실행시켜, 돌려받은 결과값을 바로 print() 함수에 넘깁니다. print() 는 고유 함수입니다; 이 함수는 따로 선언하지 않습니다. 그냥 언제든, 어디서든 사용할 수 있습니다. (많은 고유 함수들이 있고, 더 많은 함수들은 모듈로 떨어져 있습니다. 어떤 모듈들이 있는지 궁금한가요? 조금만 참아보세요.)

이 스크립트를 실행시켰을때 왜 항상 같은 결과가 나올까요? 거기에 대해 알아볼겁니다. 우선, approximate_size() 함수를 들여다 봅시다.

함수 선언

파이썬도 다른 언어들 처럼 함수를 가지고 있지만 C++의 헤더파일이나 파스칼의 interface/implementation 섹션처럼 분리되어 있지는 않습니다. 함수가 필요하다면 그냥 이렇게 선언하세요:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):

함수 선언은 def라는 키워드로 시작하고, 그 뒤에 함수 이름, 괄호가 나옵니다. 괄호 안에는 넘김값이 들어가죠. 넘김값이 여러개라면 콤마를 사용해 떨어뜨립니다.

또 함수가 그 결과를 돌려줄 때 형태를 지정하지 않는다는 점에 주의하세요. 파이썬의 함수는 그 결과값의 형태를 따로 알리지 않습니다; 심지어는 값을 돌려주는지 그러지 않는지도 알리지 않죠. (사실, 모든 파이썬 함수는 값을 돌려줍니다. 만약 함수에서 return문을 실행되면 그 값을 돌려주고, 아니라면 None이라는 빈 파이썬 값을 돌려줍니다.)

어떤 언어들에서는, 값을 돌려주는 함수는 function으로 선언을 시작하고, 값을 반환하지 않는 단순코드묶음(subroutine)은 sub라는 낱말로 시작합니다. 파이썬에는 코드묶음 개념이 없습니다. 모든것은 함수이고, 모든 함수는 None이든 어쨌든 값을 반환합니다. 그리고 모든 함수는 def로 선언을 시작합니다.

여기서 approximate_size() 함수는 sizea_kilobyte_is_1024_bytes라는 두개의 넘김값을 받습니다. 두 넘김값 모두 자료형을 명시하지 않았습니다. 파이썬에서 변수는 따로 선언하지 않습니다. 파이썬이 변수의 형태를 내부적으로 알아낸 후 기억합니다.

자바나 다른 형선언 언어들에서는, 함수 결과값과 넘김값의 자료형을 꼭 함께 적어주어야 합니다. 파이썬에서는 따로 자료형이든 어떤것이든 적어줄 필요가 없습니다. 파이썬은 그 변수나 함수에 어떤 값이 들어갔는지를 보고 그 형태를 기억합니다.

추가적이거나 이름을 가지는 넘김값

파이썬에서는 함수를 선언할 때 넘김값에 ******기본값을 줄 수 있습니다. 만약 함수가 불려질 때 넘김값이 없다면 선언시의 넘김값이 자연히 대신하게 됩니다. 그리고 이름을 함께 써준 넘김값은 어디에 위치해도 괜찮습니다.

approximate_size()함수의 선언을 다시한번 살펴보죠:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):

여기서 두번째 넘김값 a_kilobyte_is_1024_bytesTrue라는 기본값을 가지고 있습니다. 이때 이 넘김값은 추가적인 넘김값이 됩니다. 함수를 부를때 이 넘김값이 없이 넘길수 있다는 뜻이고, 그럴때 파이썬은 두번째 속성에 True를 넣은 것 처럼 행동합니다.

이제 스크립트 맨 아래를 봅시다

if __name__ == '__main__':
    print(approximate_size(1000000000000, False))  
    print(approximate_size(1000000000000))         
  1. 처음으로 approximate_size()를 부를 때는 두 넘김값을 모두 넘겼습니다. 두번째 값으로 False라는 값을 넘겼기 때문에 approximate_size()함수의 a_kilobyte_is_1024_bytesFalse가 됩니다.
  2. 두번째로 approximate_size()함수를 부를 때는 하나의 넘김값만을 넘겼습니다. 하지만 괜찮습니다, 두번째 넘김값을 넘길지는 선택할 수 있습니다. 함수를 부르는 이가 따로 두번째 넘김값을 지정하지 않았기 때문에, 그 값은 함수 선언에 따라 자연히 True가 됩니다.

또한 이름을 가지고 함수에 값을 전달할수도 있습니다.

>>> from humansize import approximate_size
>>> approximate_size(4000, a_kilobyte_is_1024_bytes=False)       
'4.0 KB'
>>> approximate_size(size=4000, a_kilobyte_is_1024_bytes=False)  
'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, size=4000)  
'4.0 KB'
>>> approximate_size(a_kilobyte_is_1024_bytes=False, 4000)       
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
>>> approximate_size(size=4000, False)                           
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
  1. 이번 approximate_size() 함수부름에는 첫번째 넘김값(size)에 4000을 넣고, a_kilobyte_is_1024_bytes 에는 False를 넣습니다.(두번째 넘김값이기도 하지만 곧 그것과는 상관없다는 걸 알게됩니다.)
  2. 이번 approximate_size() 함수 부름에는 size라는 넘김값에 4000을, a_kilobyte_is_1024_bytes에는 False를 각각 넣습니다. (함수선언시의 배열과 순서가 같지만, 역시 그래서가 아닙니다.)
  3. 이번 approximate_size() 함수 부름에는 먼저 a_kilobyte_is_1024_bytesFalse값을 넣고 size4000을 넣습니다. (순서는 상관없다고 말했었죠?)
  4. 한번 넘김값의 이름을 쓰고 나면 그 뒤로 이름을 쓰지 않는 넘김값(위치로 추정하는)은 허용되지 않습니다. 때문에 이번 함수 부름은 실패하고 맙니다. 넘김값들을 왼쪽에서 오른쪽으로 읽어가다가, 한번 넘김값 이름이 나오면, 그 뒤의 넘김값들은 계속 이름을 써 주어야 합니다.
  5. 앞과 같은 이유로 이번 부름도 실패합니다. 놀라운가요? size4000이란 값을 준 후에는 False가 뜻하는 바가 분명하지만 파이썬은 그렇게 작동하지 않습니다. 일단 하나의 넘김값에 이름을 주고 나면 그 오른쪽의 모든 넘김값들도 이름이 필요합니다.

읽히는 코드 쓰기

코드에 대한 문서를 만드는 일의 중요성을 장황하게 설명해 지루하게 하고 싶지는 않습니다. 단지 코드는 한번 쓰여지지만 여러번 읽히고, 그중 가장 중요한 독자는 코드를 작성한 후 6개월이 지났을 때의 (즉, 코드에 대해 이미 다 잊었지만 수정을 해야하는) 자신입니다. 파이썬에서 읽기좋은 코드를 작성하기는 쉽습니다, 그 혜택을 누리세요. 6개월도 지나지 않아 제게 고마워하게 될겁니다.

참조 문서

파이썬 함수를 문서화 하려면 함수에 참조 문자열을 달아주세요(줄여서 참조문이라 부르죠). 이 프로그램에서approximate_size() 함수는 참조문을 가지고 있습니다.:

def approximate_size(size, a_kilobyte_is_1024_bytes=True):
    '''Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

    '''

세따옴표는 여러줄 문자열을 의미합니다. 앞의 세 따옴표와 뒤의 세 따옴표 사이의 줄바꿈 글쇠나 들여쓰기 공백, 그리고 홑 이나 겹 따옴표 등을 포함한 모든 것은 문자열의 한 부분입니다. 세따옴표는 어디에서나 쓰이지만 참조문을 적을때 가장 많이 쓰입니다.

세따옴표는 (펄5의 qq/.../ 처럼) 홑따옴표와 겹따옴표를 같이 쓸수 있는 가장 쉬운 방법입니다.

여기서 세따옴표로 구성된 문자열 전체는 함수가 하는 일을 적어놓은 참조문입니다. 참조문이 있다면, 항상 함수 선언의 처음에 와야 합니다(함수 선언 다음줄이죠). 참조문을 다는것이 의무는 아닙니다, 하지만 할 수 있다면 꼭 참조문을 다세요. 이런 말은 프로그래밍 수업 때마다 귀가 따갑게 들었겠지만, 파이썬은 약간의 편의를 더 제공합니다:
프로그램을 실행했을 때 참조문을 함수의 속성으로 사용할 수 있다는 거죠.

많은 파이썬 개발환경이 문맥에 따른 참조문을 내비칩니다. 가령 특정 함수이름을 쓰면 작은창에 그 함수의 참조문이 나타납니다. 이 때 참조문을 잘 달아놓았다면, 큰 도움을 얻을수 있겠지만, 그렇지 않다면 속이 좀 쓰리겠죠?

import시 찾아보는 경로들

더 나아가기 전에, 라이브러리 검색 경로에 대해 간략히 언급하겠습니다. 파이썬은 모듈을 불러들일 때 몇가지 경로를 찾아봅니다. 정확하게는 sys.path에 있는 모든 디렉토리에 대해 검색합니다. 이 목록은 리스트로 표현되고, 일반적인 리스트 처리방법을 통해 쉽게 읽거나 수정할 수 있습니다. (리스트에 대해서는 고유 자료형장에서 더 알아보도록 하죠.)

>>> import sys                                                 
>>> sys.path                                                   
['', 
 '/usr/lib/python31.zip', 
 '/usr/lib/python3.1',
 '/usr/lib/python3.1/plat-linux2@EXTRAMACHDEPPATH@', 
 '/usr/lib/python3.1/lib-dynload', 
 '/usr/lib/python3.1/dist-packages', 
 '/usr/local/lib/python3.1/dist-packages']
>>> sys                                                        
<module 'sys' (built-in)>
>>> sys.path.insert(0, '/home/mark/diveintopython3/examples')  
>>> sys.path                                                   
['/home/mark/diveintopython3/examples', 
 '', 
 '/usr/lib/python31.zip', 
 '/usr/lib/python3.1', 
 '/usr/lib/python3.1/plat-linux2@EXTRAMACHDEPPATH@', 
 '/usr/lib/python3.1/lib-dynload', 
 '/usr/lib/python3.1/dist-packages', 
 '/usr/local/lib/python3.1/dist-packages']
  1. sys 모듈을 불러오면, 그 안의 모든 함수와 속성을 사용할 수 있습니다.
  2. sys.path에는 현재 검색 경로로 지정된 디렉토리 목록이 들어있습니다. (운영체제나 파이썬 버전, 설치 위치에 따라 책에서와는 다르게 보일수 있습니다.) 파이썬은 불러오려는 모듈과 같은 이름을 가진 .py 파일이 있는지 확인하기 위해 폴더들을 쭉 검색합니다.
  3. 예, 뻥을 좀 쳤어요; 사실은 좀 더 복잡합니다. 모든 모듈이 .py 파일에 저장되어있는것은 아니기 때문이죠. 어떤 것들은 고유모듈입니다; 얘네들은 파이썬 내부에 흡수되어 있습니다. 고유 모듈은 다른 모듈과 똑같지만 파이썬 소스 코드는 볼수 없습니다. 왜냐면 파이썬으로 작성되지 않았기 때문이죠! (파이썬과 이런 고유 모듈들은 C로 작성되었습니다.)
  4. 실행시 sys.path에 디렉토리명을 추가하면 파이썬 검색 경로에 새 디렉토리가 들어갑니다. 다음 번 부터 모듈을 불러올 때는 이 디렉토리도 마찬가지로 눈여겨 볼겁니다. 이 효과는 파이썬이 켜있는 동안 지속됩니다.
  5. sys.path.insert(0, new_path)라고 쓰면, 새 디렉토리가 sys.path의 첫 항목으로 추가됩니다. 파이썬 검색경로의 처음에 있는거죠. 이렇게 하는게 대부분 더 낫습니다. 같은 이름의 파일이 여러 개 있을 때(예를 들어, 특정한 라이브러리의 파이썬2 용 버전이 있지만 파이썬3 용 버전을 쓰고 싶을때), 이렇게 하면 파이썬에 탑재된 모듈보다 직접 추가한 모듈을 먼저 찾게 됩니다.

모든것은 객체다

노파심에 다시 얘기하면 파이썬에서 함수는 속성을 가지고, 그 속성은 실행시 사용할 수 있습니다. 파이썬의 다른 모든 것들처럼 함수도 객체입니다.

대화형 파이썬 셸을 키고 다음을 따라해 보세요:

>>> import humansize                               
>>> print(humansize.approximate_size(4096, True))  
4.0 KiB
>>> print(humansize.approximate_size.__doc__)      
Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string

  1. 첫번째 줄은 humansize 프로그램을 모듈(필요시 꺼내 쓰거나 더 큰 파이썬 프로그램에서 사용할 수 있는 코드 묶음)로 받아옵니다. 한번 모듈을 받아오면, 그 모듈이 공개하는 모든 함수나 클래스, 속성 등을 참조할수 있습니다. 모듈 또한 다른 모듈의 기능을 받아와 쓸수 있고, 이를 바로반응형 파이썬 셸에서 해볼수도 있습니다. 이런 컨셉이 얼마나 중요한지 이 책을 읽어나가며 이해하게 될겁니다.
  2. 모듈안에서 정의된 함수를 사용하려면, 그 모듈의 이름도 같이 써주어야 합니다. 그냥 approximate_size라고 할수가 없죠; humansize.approximate_size여야만 합니다. 자바의 클래스를 써본 적이 있다면 왠지 친숙할겁니다.
  3. 함수 자체를 불러오지 않고, __doc__이라는 한가지 속성만을 불러와 봅시다.

파이썬의 import는 펄의 require과 비슷합니다. 한번 파이썬 모듈을 부르면, 모듈.함수 로 그 함수를 부릅니다; 펄에서 모듈을 한번 요청(require)하면, 모듈::함수 라고 써 그 함수를 부릅니다.

객체가 뭔가요?

파이썬안의 모든것은 다 객체고, 동시에 속성과 메소드를 가집니다. 모든 함수는 선언시 소스코드에 써두었던 참조문을 돌려주는 고유 속성 __doc__을 가지고 있습니다. sys 모듈은 (다른것도 있지만) path라는 속성을 가지고 있습니다. 등등등.

여전히 기본적인 질문에 대한 답은 나오질 않았네요: 객체란 무엇일까요? 여러 프로그래밍 언어들은 서로 다르게 객체를 정의합니다. 몇몇 언어에서는 모든 객체가 무조건 속성과 메소드를 가져야 합니다; 다른 언어에서는 모든 객체가 하위클래스를 가질수 있어야 합니다. 파이썬의 정의는 조금 느슨합니다. 객체는 속성과 메소드를 가질수 있지만, 어떤 객체는 둘 다 안 가지고 있을 수도 있습니다. 모든 객체가 하위클래스를 가지지는 않습니다. 하지만 무엇이나 변수에 저장되거나 함수에 넘겨질 수 있다는 점에서 모든것은 객체입니다.

아마 다른 프로그래밍 언어에서 “제한적이지 않은 객체(first-class object)” 라는 말을 들어보았을 겁니다. 파이썬에서 함수는 제한적이지 않은 객체 입니다. 함수를 다른 함수의 넘김값으로 사용할수 있습니다. 모듈은 제한적이지 않은 객체입니다. 함수에 모듈 전체를 넘기는것도 가능합니다. 클래스도 제한적이지 않은 객체이고, 그 클래스 각각의 인스턴스도 제한적이지 않은 객체입니다.

파이썬의 모든것은 다 객체다: 이 개념은 매우 중요하기 때문에 처음 몇번은 계속 되풀이 해서 얘기할겁니다. 문자열은 객체입니다. 리스트는 객체입니다. 함수는 객체입니다. 클래스는 객체입니다. 클래스 인스턴스는 객체입니다. 또 모듈도 객체입니다.

코드 들여쓰기

파이썬의 함수는 시작이나 끝을 나타내는 특정한 코드를 가지지 않습니다. 함수의 시작과 끝을 나타내는 대괄호도 없습니다. 콜론(:)과 들여쓰기 만을 이용해 이를 나타냅니다.

def approximate_size(size, a_kilobyte_is_1024_bytes=True):  
    if size < 0:                                            
        raise ValueError('number must be non-negative')     
                                                            
    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:                       
        size /= multiple
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)

    raise ValueError('number too large')
  1. 코드 블록은 들여쓰기에 의해 결정됩니다. 여기서 코드 블록이라 함은, if문, for문, while문 등을 말하는 겁니다. 들여쓰면 블록이 시작되고 내어쓰면 그 블록이 끝납니다. 대 소 괄호나, 키워드를 따로 써주지 않습니다. 그 말은 공백이 의미를 가지기에 항상 일관적이어야 한다는 뜻입니다. 이 예제에서 함수코드는 네칸을 띄워 씁니다. 꼭 네칸일 필요는 없지만 계속 똑같아야 합니다. 내어쓴 첫줄은 함수구문이 끝났음을 의미합니다.
  2. 파이썬에서 if 구문은 코드블럭과 함께합니다. 만일 if 식을 계산해 참이라면 바로 들여쓴 블록이 실행되고, 거짓이고 else 블럭이 있다면, 그리로 이동합니다. 식에 괄호가 없는게 보이시죠?
  3. 이 줄은 if 코드 블럭입니다. 만일 size < 0라면, 여기 raise 문이 (ValueError 형태의) 예외를 일으킵니다.
  4. 여기는 함수의 끝이 아닙니다. 완전히 빈 줄은 세지 않습니다. 빈 줄은 코드를 더 읽기 좋게 만들뿐, 코드블록을 구분짓는 표시가 아닙니다. 함수는 다음줄로 이어집니다.
  5. for 되돌이 역시 코드블럭의 시작을 의미합니다. 같은 크기로 들여쓴다면 한 코드블럭에 여러 줄을 담더라도 문제 없습니다. 이 for 되돌이는 세줄의 코드를 담고 있습니다. 코드블럭에 여러줄을 담았다 해서 특별한 문법같은건 없습니다. 그냥 들여쓰고 이어가세요.

처음 조금은 거부감이 들기도 하고 포트란과 비교되기도 하겠지만, 점차 익숙해지고 그 장점이 눈에 보일겁니다. 각자의 스타일에 따라 달리 들여쓰기를 않고, 언어의 문법으로 정의해 버렸기에 가지는 큰 장점중 하나는 모든 파이썬 프로그램이 다 비슷해보인다는 거죠. 이는 다른 사람들의 파이썬 코드를 읽고 이해하기 쉽게 만들어 줍니다.

파이썬은 줄바꿈 문자를 사용해 문장을 나누고, 콜론과 들여쓰기를 이용해 코드블럭을 나눕니다. C++ 과 자바는 세미콜론을 사용해 문장을 나누고 대괄호를 써 코드 블럭을 나눕니다.

예외

파이썬 어디에나 예외가 있습니다. 사실상 파이썬 라이브러리의 모든 모듈이 예외처리를 합니다. 그리고 파이썬은 많은 경우에 예외를 일으킵니다. 이 책을 읽어나가면서도 많은 예외상황이 나타날 겁니다.

예외가 뭘까요? 대부분의 경우 뭐가 잘못됐는지 표시하는 에러입니다. (모든 예외가 다 에러는 아니지만, 지금은 생각하지 않기로 하죠.) 어떤 프로그래밍 언어는 문제를 확인하게끔 에러 반환 코드를 지원합니다. 하지만 파이썬은 프로그램의 행동에 영향을 미치는, 예외상황을 만들어 낼 수 있습니다.

파이썬 셸에서 에러가 발생하면, 셸은 상황에 대한 자세한 설명과 왜 문제가 생겼는지를 알려줍니다. 그러고는 멈추죠. 이런 상황을 예상못한unhandled 예외라 합니다. 문제가 생겼음을 알아채고 처리할수 있는 코드가 없는거죠. 그러면 프로그램은 다시 맨위로 올라와, 오류 고치는데 도움이 되는 정보를 주고는 멉춥니다. 셸에서라면 별 문제 될게 없지만, 어디선가 돌아가고 있는 파이썬 프로그램이 이런 일을 맞닥드렸을 때 예외상황을 처리할 코드가 없다면 모든 프로그램이 끼익! 하고 멈추게 됩니다. 그렇게 되길 원할수도 있지만 아닐수도 있죠.

파이썬 함수는 자바와 달리 어떤 예외를 발생시킬지 정의하지 않습니다. 어떤 예외상황이 있을지는 스스로 생각해내야 합니다.

예외상황이 언제나 프로그램의 충돌로 끝나지는 않습니다. 예외를 처리할 수도 있습니다. 가끔은 없는 변수에 값을 넣을 때처럼 진짜오류!가 있기도 하지만, 어떤 때는 예외가 일어날 것을 미리 알아챌수 있습니다. 없는 파일을 연다거나 불러오려는 모듈이 깔려 있지 않다거나 데이터베이스에 연결하려는데 그 데이터베이스가 없거나 제대로 된 보안키를 가지고 있지 않거나하는 상황 등, 코드의 어떤 부분에서 오류가 날지 알고 있다면 이런 예외를 처리하기 위해 try...except 구문을 써야합니다.

파이썬은 예외를 처리하기 위해 try...except 구문을 쓰고, 예외를 강제로 발생시키기 위해 raise 문을 씁니다. 자바와 C++은 예외를 처리하기 위해 try...catch 구문을 쓰고, 예외를 발생시키기 위해 throw 문을 씁니다.

여기서 approximate_size() 함수는 두가지 경우 예외를 발생시키는데 하나는 size가 처리할수 없을만큼 큰 값이 들어왔을때이고 다른 하나는 0보다 작은 값이 들어왔을 때입니다.

if size < 0:
    raise ValueError('number must be non-negative')

예외 일으키기는 매우 간단합니다. raise 문 뒤에 예외이름을 쓰고 필요하다면 오류잡기에 도움되는 문장을 써넣으면 됩니다. 문법이 함수를 부를때와 비슷하죠? (실제로는 예외처리가 클래스로 구현되어 있고, 여기 raise 문은 ValueError 클래스의 인스턴스를 만들어 초기화 할 때 'number must be non-negative' 문자열을 넣어줍니다. 하지만 나중엔 직접 예외처리를 만들어 기본 예외처리를 덮어쓰도록 해보죠!---더 쉽게,이해 어려움)

모든 함수에 예외처리를 만들지 않아도 됩니다. 어떤 함수가 예외를 처리하지 못한다면, 예외는 함수를 불러낸 함수로 올라갑니다. 그런식으로 단계를 거쳐 올라가죠. 그 와중에 예외가 처리되지 않는다면 프로그램은 충돌을 일으키고 파이썬이 오류 추적용 기본 메시지를 뱉고는 끝이 납니다. 다시 말하지만, 그렇게 하는것도 한 방법입니다. 어떤 프로그램이냐에 달렸죠.

부르기 실패 알아채기

파이썬은 모듈을 불러 오려다 실패했을 때 대비해 ImportError 예외를 미리 정의해 놓았습니다. 이 예외는 여러 이유에서 발생하는데, 보통은 불러오려는 모듈이 반입 검색 경로에 없기 때문에 발생합니다. 이 예외를 써 프로그램에 기능을 추가할수 있습니다. 예로 프로그램이 문자 인코딩을 자동으로 찾아주는 chardet 이라는 라이브러리를 불려오려다 설치되어있지않아 실패하더라도 try..except 구문을 사용해 우아하게 넘어갈 수 있습니다.

try:
  import chardet
except ImportError:
  chardet = None

나중에 if 문으로 chardet 모듈이 있는지 간단히 체크해볼수 있습니다:

if chardet:
  # do something
else:
  # continue anyway

두개의 모듈이 다 일반적인 API로 이루어져 있지만, 하나가 다른 것보다 더 나을 때(아마 더 빠르거나, 메모리를 적게 먹겠죠.) 도 ImportError를 씁니다. 이 때 한 모듈을 불러오려다 실패하면 다른 모듈을 부릅니다. 예로 XML 장에서는 ElementTree API를 사용한 두 모듈에 대해 이야기 합니다. 그 중 lxml 모듈은 사용자가 직접 다운받아 깔아야 하는 부가모듈이고, xml.etree.ElementTree는 더 느리긴 하지만 파이썬3의 기본 라이브러리에 포함된 모듈입니다.

try:
    from lxml import etree
except ImportError:
    import xml.etree.ElementTree as etree

try..except 구문 마지막에 아무개 모듈을 불러와 etree라 부르기로 합니다. 두 모듈 다 일반 API를 썼기 때문에, 뒤쪽의 코드에서 어떤 모듈을 불러왔는지 알 필요가 없습니다. 그리고 불러온 모듈은 항상 etree라 부르기로 했기 때문에 나중에 if 문을 써 각각의 모듈 이름에 대한 경우의 수만큼 따로 정의할 필요가 없습니다.

고정되지 않는(좀더 나은표현?) 변수

approximate_size() 함수 선언의 다른부분을 살펴봅시다:

multiple = 1024 if a_kilobyte_is_1024_bytes else 1000

multiple변수를 선언하지 않고 값을 집어 넣었습니다. 파이썬에서는 이렇게 하는것이 허용됩니다. 하지만 값이 들어가지 않은 변수를 참조하려는건 허용되지 않습니다. 이 때는 NameError 예외가 발생합니다.

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = 1
>>> x
1

언젠가 파이썬에게 고마워할겁니다.

대소문자 구분은 확실히

파이썬의 모든 이름은 다 대소문자를 구분합니다: 이름을 가지는 것에는 변수와 함수와 클래스와 모듈과 예외가 있습니다. 이들을 얻으려거나 지정하거나 부르거나 생성하거나 가져오거나 발생시킬 때는 대소문자를 구별해 써주세요.

>>> an_integer = 1
>>> an_integer
1
>>> AN_INTEGER
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'AN_INTEGER' is not defined
>>> An_Integer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'An_Integer' is not defined
>>> an_inteGer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'an_inteGer' is not defined

이럴때는 항상 에러가 납니다.

스크립트 실행하기

파이썬에서 모듈은 객체이고 몇가지 유용한 속성을 가집니다. 이를 이용하면만들고 있는 모듈을 간단히 테스트 해볼 수 있습니다. 커맨드 라인에서 파이썬 파일을 실행시킬때 특별한 구문을 추가하면 됩니다. humansize.py의 마지막 몇줄을 살펴보죠:


if __name__ == '__main__':
    print(approximate_size(1000000000000, False))
    print(approximate_size(1000000000000))

C처럼 파이썬도 값을 비교할 때 ==를 쓰고 값을 넣을때 =을 씁니다. 하지만 C와는 달리 비교하는 줄에서는 값을 집어넣을수 없습니다. 이렇게 하면 값을 비교하려다 넣어버리는 사고를 막을수 있죠.

자, 이 독특한 if 문은 뭘까요? 모듈은 객체죠, 그리고 모든 모듈은 __name__이란 고유속성을 가집니다. 모듈의 __name__ 값은 모듈을 쓰는 방법에 따라 달라집니다. 만약 모듈을 받아 온거라면, __name__ 은 이 모듈이 든 (디렉토리나 확장자를 제외한 순수한) 파일의 이름입니다.

>>> import humansize
>>> humansize.__name__
'humansize'

하지만 모듈을 직접 실행할수도 있겠죠. 그랬을 때 __name__ 은 특별한 기본값 __main__이 됩니다. 파이썬이 이 if 문의 값을 계산해 식이 맞다는걸 확인하고, if 구문의 내용을 실행합니다. 그러면 두개의 값이 출력됩니다.

c:\home\diveintopython3> c:\python31\python.exe humansize.py
1.0 TB
931.3 GiB

자 이렇게 해서 우리의 첫 파이썬 프로그램을 다 둘러봤네요!

더 읽을거리

© 2001–11 Mark Pilgrim