8. 에러와 예외
**************

지금까지 에러 메시지가 언급되지는 않았지만, 예제들을 직접 해보았다면
아마도 몇몇 개를 보았을 것입니다. (적어도) 두 가지 구별되는 에러들이
있습니다; *문법 에러* 와 *예외*.


8.1. 문법 에러
==============

문법 에러는, 파싱 에러라고도 알려져 있습니다, 아마도 여러분이 파이썬을
배우고 있는 동안에는 가장 자주 만나는 종류의 불평일 것입니다:

   >>> while True print('Hello world')
     File "<stdin>", line 1
       while True print('Hello world')
                      ^
   SyntaxError: invalid syntax

파서는 문제가 되는 줄을 다시 보여주고 줄에서 에러가 감지된 가장 앞의
위치를 가리키는 작은 '화살표'를 표시합니다. 에러는 화살표 *앞에 오는*
토큰이 원인입니다 (또는 적어도 그곳에서 감지되었습니다): 이 예에서, 에
러는 함수 "print()" 에서 감지되었는데, 그 앞에 콜론 ("':'") 이 빠져있
기 때문입니다. 파일 이름과 줄 번호가 인쇄되어서, 입력이 스크립트로부터
올 때 찾을 수 있도록 합니다.


8.2. 예외
=========

문장이나 표현식이 문법적으로 올바르다 할지라도, 실행하려고 하면 에러를
일으킬 수 있습니다. 실행 중에 감지되는 에러들을 *예외* 라고 부르고 무
조건 치명적이지는 않습니다: 파이썬 프로그램에서 이것들을 어떻게 다루는
지 곧 배우게 됩니다. 하지만 대부분의 예외는 프로그램이 처리하지 않아서
, 여기에서 볼 수 있듯이 에러 메시지를 만듭니다:

   >>> 10 * (1/0)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ZeroDivisionError: division by zero
   >>> 4 + spam*3
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: name 'spam' is not defined
   >>> '2' + 2
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: can only concatenate str (not "int") to str

에러 메시지의 마지막 줄은 어떤 일이 일어났는지 알려줍니다. 예외는 여러
형으로 나타나고, 형이 메시지 일부로 인쇄됩니다: 이 예에서의 형은
"ZeroDivisionError", "NameError", "TypeError" 입니다. 예외 형으로 인쇄
된 문자열은 발생한 내장 예외의 이름입니다. 이것은 모든 내장 예외들의
경우는 항상 참이지만, 사용자 정의 예외의 경우는 (편리한 관례임에도 불
구하고) 꼭 그럴 필요는 없습니다. 표준 예외 이름은 내장 식별자입니다 (
예약 키워드가 아닙니다).

줄의 나머지 부분은 예외의 형과 원인에 기반을 둔 상세 명세를 제공합니다
.

에러 메시지의 앞부분은 스택 트레이스의 형태로 예외가 일어난 위치의 문
맥을 보여줍니다. 일반적으로 소스의 줄들을 나열하는 스택 트레이스를 포
함하고 있습니다; 하지만, 표준 입력에서 읽어 들인 줄들은 표시하지 않습
니다.

내장 예외 는 내장 예외들과 그 들의 의미를 나열하고 있습니다.


8.3. 예외 처리하기
==================

선택한 예외를 처리하는 프로그램을 만드는 것이 가능합니다. 다음 예를 보
면, 올바를 정수가 입력될 때까지 사용자에게 입력을 요청하지만, 사용자가
프로그램을 인터럽트 하는 것을 허용합니다 ("Control-C" 나 그 외에 운영
체제가 지원하는 것을 사용해서); 사용자가 만든 인터럽트는
"KeyboardInterrupt" 예외를 일으키는 형태로 나타남에 유의하세요.

   >>> while True:
   ...     try:
   ...         x = int(input("Please enter a number: "))
   ...         break
   ...     except ValueError:
   ...         print("Oops!  That was no valid number.  Try again...")
   ...

"try" 문은 다음과 같이 동작합니다.

* 먼저, *try 절* ("try" 와 "except" 사이의 문장들) 이 실행됩니다.

* 예외가 발생하지 않으면, *except 절* 을 건너뛰고 "try" 문의 실행은 종
  료됩니다.

* try 절을 실행하는 동안 예외가 발생하면, 절의 남은 부분들을 건너뜁니
  다. 그런 다음 형이 "except" 키워드 뒤에 오는 예외 이름과 매치되면,
  그 except 절이 실행되고, 그런 다음 실행은 "try" 문 뒤로 이어집니다.

* except 절에 있는 예외 이름들과 매치되지 않는 예외가 발생하면, 외부에
  있는 "try" 문으로 전달됩니다; 처리기가 발견되지 않으면, *처리되지 않
  은 예외* 이고 위에서 보인 것과 같은 메시지를 출력하면서 실행이 멈춥
  니다.

각기 다른 예외에 대한 처리기를 지정하기 위해, "try" 문은 하나 이상의
except 절을 가질 수 있습니다. 최대 하나의 처리기가 실행됩니다. 처리기
는 해당하는 try 절에서 발생한 예외만 처리할 뿐 같은 "try" 문의 다른 처
리기가 일으킨 예외를 처리하지는 않습니다. except 절은 괄호가 있는 튜플
로 여러 개의 예외를 지정할 수 있습니다, 예를 들어:

   ... except (RuntimeError, TypeError, NameError):
   ...     pass

"except" 절에 있는 클래스는 예외와 같은 클래스이거나 베이스 클래스일
때 매치됩니다 (하지만 다른 방식으로는 매치되지 않습니다 --- 자식 클래
스를 나열한 except 절은 베이스 클래스와 매치되지 않습니다). 예를 들어,
다음과 같은 코드는 B, C, D를 그 순서대로 인쇄합니다:

   class B(Exception):
       pass

   class C(B):
       pass

   class D(C):
       pass

   for cls in [B, C, D]:
       try:
           raise cls()
       except D:
           print("D")
       except C:
           print("C")
       except B:
           print("B")

except 절이 뒤집히면 ("except B" 가 처음에 오도록), B, B, B를 인쇄하게
됨에 주의하세요 --- 처음으로 매치되는 절이 실행됩니다.

마지막 except 절은 예외 이름을 생략할 수 있는데, 와일드카드 역할을 합
니다. 이것을 사용할 때는 극도의 주의를 필요로 합니다. 이런 식으로 실제
프로그래밍 에러를 가리기 쉽기 때문입니다! 에러 메시지를 인쇄한 후에 예
외를 다시 일으키는데 사용될 수도 있습니다 (호출자도 예외를 처리할 수
있도록):

   import sys

   try:
       f = open('myfile.txt')
       s = f.readline()
       i = int(s.strip())
   except OSError as err:
       print("OS error: {0}".format(err))
   except ValueError:
       print("Could not convert data to an integer.")
   except:
       print("Unexpected error:", sys.exc_info()[0])
       raise

"try" ... "except" 문은 선택적인 *else 절* 을 갖는데, 있다면 모든
except 절 뒤에와야 합니다. try 절이 예외를 일으키지 않을 때 실행되어야
만 하는 코드에 유용합니다. 예를 들어:

   for arg in sys.argv[1:]:
       try:
           f = open(arg, 'r')
       except OSError:
           print('cannot open', arg)
       else:
           print(arg, 'has', len(f.readlines()), 'lines')
           f.close()

"else" 절의 사용이 "try" 절에 코드를 추가하는 것보다 좋은데, "try" ...
"except" 문에 의해 보호되고 있는 코드가 일으키지 않은 예외를 우연히 잡
게 되는 것을 방지하기 때문입니다.

예외가 발생할 때, 연관된 값을 가질 수 있는데, 예외의 *인자* 라고도 알
려져 있습니다. 인자의 존재와 형은 예외 형에 의존적입니다.

except 절은 예외 이름 뒤에 변수를 지정할 수 있습니다. 변수는 인자들이
"instance.args" 에 저장된 예외 인스턴스에 연결됩니다. 편의를 위해, 예
외 인스턴스는 "__str__()" 를 정의해서, ".args" 를 참조하지 않고도 인자
들을 직접 인쇄할 수 있습니다. 예외를 일으키기 전에 인스턴스를 먼저 만
들고 필요한 어트리뷰트들을 추가할 수도 있습니다.

   >>> try:
   ...     raise Exception('spam', 'eggs')
   ... except Exception as inst:
   ...     print(type(inst))    # the exception instance
   ...     print(inst.args)     # arguments stored in .args
   ...     print(inst)          # __str__ allows args to be printed directly,
   ...                          # but may be overridden in exception subclasses
   ...     x, y = inst.args     # unpack args
   ...     print('x =', x)
   ...     print('y =', y)
   ...
   <class 'Exception'>
   ('spam', 'eggs')
   ('spam', 'eggs')
   x = spam
   y = eggs

예외가 인자를 가지면, 처리되지 않은 예외 메시지의 마지막 부분('상세 명
세')에 인쇄됩니다.

예외 처리기는 단지 try 절에 직접 등장하는 예외뿐만 아니라, try 절에서
(간접적으로라도) 호출되는 내부 함수들에서 발생하는 예외들도 처리합니다
. 예를 들어:

   >>> def this_fails():
   ...     x = 1/0
   ...
   >>> try:
   ...     this_fails()
   ... except ZeroDivisionError as err:
   ...     print('Handling run-time error:', err)
   ...
   Handling run-time error: division by zero


8.4. 예외 일으키기
==================

"raise" 문은 프로그래머가 지정한 예외가 발생하도록 강제할 수 있게 합니
다. 예를 들어:

   >>> raise NameError('HiThere')
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: HiThere

"raise" 에 제공하는 단일 인자는 발생시킬 예외를 가리킵니다. 예외 인스
턴스이거나 예외 클래스 ("Exception" 를 계승하는 클래스) 이어야 합니다.
예외 클래스가 전달되면, 묵시적으로 인자 없이 생성자를 호출해서 인스턴
스를 만듭니다:

   raise ValueError  # shorthand for 'raise ValueError()'

만약 예외가 발생했는지는 알아야 하지만 처리하고 싶지는 않다면, 더 간단
한 형태의 "raise" 문이 그 예외를 다시 일으킬 수 있게 합니다:

   >>> try:
   ...     raise NameError('HiThere')
   ... except NameError:
   ...     print('An exception flew by!')
   ...     raise
   ...
   An exception flew by!
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   NameError: HiThere


8.5. 예외 연쇄
==============

The "raise" statement allows an optional "from" which enables chaining
exceptions. For example:

   # exc must be exception instance or None.
   raise RuntimeError from exc

이것은 예외를 변환할 때 유용할 수 있습니다. 예를 들면:

   >>> def func():
   ...     raise IOError
   ...
   >>> try:
   ...     func()
   ... except IOError as exc:
   ...     raise RuntimeError('Failed to open database') from exc
   ...
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
     File "<stdin>", line 2, in func
   OSError

   The above exception was the direct cause of the following exception:

   Traceback (most recent call last):
     File "<stdin>", line 4, in <module>
   RuntimeError: Failed to open database

"except"나 "finally" 섹션 안에서 예외가 발생하면 예외 연쇄가 자동으로
일어납니다. "from None" 관용구를 사용하여 예외 연쇄를 비활성화 할 수
있습니다:

>>> try:
...     open('database.sqlite')
... except OSError:
...     raise RuntimeError from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError

연쇄 메커니즘에 대한 자세한 내용은, 내장 예외를 참조하십시오.


8.6. 사용자 정의 예외
=====================

새 예외 클래스를 만듦으로써 프로그램은 자신의 예외에 이름을 붙일 수 있
습니다 (파이썬 클래스에 대한 자세한 내용은 클래스 를 보세요). 예외는
보통 직접적으로나 간접적으로 "Exception" 클래스를 계승합니다.

Exception classes can be defined which do anything any other class can
do, but are usually kept simple, often only offering a number of
attributes that allow information about the error to be extracted by
handlers for the exception.

대부분의 예외는 표준 예외들의 이름들과 유사하게, "Error" 로 끝나는 이
름으로 정의됩니다.

많은 표준 모듈들은 그들이 정의하는 함수들에서 발생할 수 있는 그 자신만
의 예외들을 정의합니다. 클래스에 관한 더 자세한 정보는 클래스 장에서
다룹니다.


8.7. 뒷정리 동작 정의하기
=========================

"try" 문은 또 다른 선택적 절을 가질 수 있는데 모든 상황에 실행되어야만
하는 뒷정리 동작을 정의하는 데 사용됩니다. 예를 들어:

   >>> try:
   ...     raise KeyboardInterrupt
   ... finally:
   ...     print('Goodbye, world!')
   ...
   Goodbye, world!
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   KeyboardInterrupt

"finally" 절이 있으면, "try" 문이 완료되기 전에 "finally" 절이 마지막
작업으로 실행됩니다. "finally" 절은 "try" 문이 예외를 생성하는지와 관
계없이 실행됩니다. 다음은 예외가 발생할 때 더 복잡한 경우를 설명합니다
:

* "try" 절을 실행하는 동안 예외가 발생하면, "except" 절에서 예외를 처
  리할 수 있습니다. 예외가 "except" 절에서 처리되지 않으면, "finally"
  절이 실행된 후 예외가 다시 발생합니다.

* "except"나 "else" 절 실행 중에 예외가 발생할 수 있습니다. 다시,
  "finally" 절이 실행된 후 예외가 다시 발생합니다.

* If the "finally" clause executes a "break", "continue" or "return"
  statement, exceptions are not re-raised.

* "try" 문이 "break", "continue" 또는 "return" 문에 도달하면,
  "finally" 절은 "break", "continue" 또는 "return" 문 실행 직전에 실행
  됩니다.

* "finally" 절에 "return" 문이 포함되면, 반환 값은 "try" 절의 "return"
  문이 주는 값이 아니라, "finally" 절의 "return" 문이 주는 값이 됩니다
  .

예를 들면:

   >>> def bool_return():
   ...     try:
   ...         return True
   ...     finally:
   ...         return False
   ...
   >>> bool_return()
   False

더 복잡한 예:

   >>> def divide(x, y):
   ...     try:
   ...         result = x / y
   ...     except ZeroDivisionError:
   ...         print("division by zero!")
   ...     else:
   ...         print("result is", result)
   ...     finally:
   ...         print("executing finally clause")
   ...
   >>> divide(2, 1)
   result is 2.0
   executing finally clause
   >>> divide(2, 0)
   division by zero!
   executing finally clause
   >>> divide("2", "1")
   executing finally clause
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "<stdin>", line 3, in divide
   TypeError: unsupported operand type(s) for /: 'str' and 'str'

보인 바와 같이, "finally" 절은 모든 경우에 실행됩니다. 두 문자열을 나
눠서 발생한 "TypeError" 는 "except" 절에 의해 처리되지 않고 "finally"
절이 실행된 후에 다시 일어납니다.

실제 세상의 응용 프로그램에서, "finally" 절은 외부 자원을 사용할 때,
성공적인지 아닌지와 관계없이, 그 자원을 반납하는 데 유용합니다 (파일이
나 네트워크 연결 같은 것들).


8.8. 미리 정의된 뒷정리 동작들
==============================

어떤 객체들은 객체가 더 필요 없을 때 개입하는 표준 뒷정리 동작을 정의
합니다. 그 객체를 사용하는 연산의 성공 여부와 관계없습니다. 파일을 열
고 그 내용을 화면에 인쇄하려고 하는 다음 예를 보세요.

   for line in open("myfile.txt"):
       print(line, end="")

이 코드의 문제점은 이 부분이 실행을 끝낸 뒤에도 예측할 수 없는 기간 동
안 파일을 열린 채로 둔다는 것입니다. 간단한 스크립트에서는 문제가 되지
않지만, 큰 응용 프로그램에서는 문제가 될 수 있습니다. "with" 문은 파일
과 같은 객체들이 즉시 올바르게 뒷정리 되도록 보장하는 방법을 제공합니
다.

   with open("myfile.txt") as f:
       for line in f:
           print(line, end="")

문장이 실행된 후에, 줄을 처리하는 데 문제가 발생하더라도, 파일 *f* 는
항상 닫힙니다. 파일과 같이, 미리 정의된 뒷정리 동작들을 제공하는 객체
들은 그들의 설명서에서 이 사실을 설명합니다.
