파이썬 예외 종류와 예외 처리

파이썬 예외 종류와 예외 처리

예외 종류(Exceptions)

파이썬에는 다양한 오류를 설명하는 여러 가지의 내장 예외 종류(exceptions)가 있다.

ValueError

ValueError는 작업 중 유효하지 않은 값을 마주 했을 때 일어난다.

>>> int("not a number")
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
	int("not a number")
ValueError: invalid literal for int() with base 10: 'not a number'

위 예제에서 int() 함수를 이용해 정수로 변환될 수 없는 문자열 값을 넣으므로 ValueError가 발생한 것을 확인할 수 있다.

TypeError

TypeError잘못된 유형의 값으로 작업이 수행될 경우 발생한다. 예를 들어, 다음과 같이 문자열에 정수를 더하려고 했을 때 TypeError가 발생한다.

>>> "1" + 2
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
	"1" + 2
TypeError: can only concatenate str (not "int") to str

NameError

NameError사용하려는 변수의 이름이 아직 정의되어 있지 않을 때 발생한다.

>>> print(does_not_exist)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
	print(does_not_exist)
NameError: name 'does_not_exist' is not defined

ZeroDivisionError

ZeroDivisionError나눗셈 연산에서 어떤 수를 나누는 수가 0일 때 발생한다.

>>> 1 / 0
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
	1 / 0
ZeroDivisionError: division by zero

OverflowError

OverflowError산술 연산의 결과가 너무 클 때 발생한다. 예를 들어, 다음과 같이 2.01_000_000만큼 거듭제곱 하려고 할 때 처럼 말이다.

>>> pow(2.0, 1_000_000)
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
	pow(2.0, 1_000_000)
OverflowError: (34, 'Result too large')

더 많은 예외 종류는 공식 문서에서 확인할 수 있다.


예외 처리

코드를 작성하다보면 특정 부분에서 '어떤 에러가 발생할 수 있겠다'라는 예상을 할 수 있다. 이런 경우에, 에러가 발생하게 두어서 프로그램이 고장나도록 두는 것이 아니라, 에러 발생 시 에러를 잡고(catch) 다른 작업을 수행할 수 있다.

try & except 키워드

파이썬에서는 프로그램에 에러를 발생하는 것을 방지하기 위해 tryexcept 키워드를 제공한다. 기본 사용법은 다음과 같다.

try:
	number = int(input("Enter an integer: "))
except ValueError:
	print("That was not an integer")

보이는 것처럼 사용자가 input() 함수를 통해 정수를 입력하도록 하는 코드이다. 만약 정수가 아닌 값을 입력한다면 int() 함수에서 ValueError를 일으킨다. 하지만 try-except문을 사용함으로써,ValueError 발생 시 에러를 캐치print()was not an intenger"가 출력된다. 유효한 정수 값을 입력했다면, try문 안에 있는 코드가 실행되고 except문에 코드는 절대 실행되지 않는다.

반면에, 만약 except절에서 지정하지 않은 에러가 발생한다면, 프로그램은 에러를 일으키고 실행을 멈춘다. 이런 상황을 대비해, 여러 종류의 예외를 명시해줄 수 있다.

def divide(num1, num2):
	try:
		print(num1 / num2)
	except (TypeError, ZeroDivisionError):
		print("encountered a problem")

위 예제처럼 except절에서 여러 예외 종류를 사용시, 명시해준 모든 에러를 캐치except문 안에 있는 코드를 실행하게 된다. 하지만, 여러 종류의 예외를 한 번에 사용하면 에러 발생시 정확히 어떤 이유로, 어디에서 에러가 발생했는지 추적하기 힘들 수 있다. 이런 경우에는 여러 예외 타입들을 개별적으로 캐치할 수 있다.

def divide(num1, num2):
	try:
		print(num1 / num2)
	except TypeError:
		print("Both arguments must be numbers")
	except ZeroDivisionError:
		print("num2 must not be 0")

이름 없는 except절 사용하기

except 키워드를 사용할 때, 특정한 예외 이름 없이, 키워드만으로도 사용이 가능하다. 다음 예제에서는 어떤 에러가 발생해도 except문 안에 코드가 실행된다.

try:
	# Do lots of hazardous things that might break
except:
	print("Something bad happened!")

이런 방식의 except 키워드 사용은 모든 에러를 잡아주고 에러를 절대 발생시키지 않기 때문에 좋은 방법이라고 생각할 수 있다. 하지만 사실 이런 류의 코드는 좋지 않은 생각이며 눈살을 찌푸리게 만드는 패턴이라고 할 수 있다.

여러 이유가 있겠지만, 가장 큰 이유 중 하나는, 모든 에러를 발생하지 않게 하고, 모르고 넘어감으로써 나도 모르게 코드 안에 버그를 심을 수도 있다는 것이다. 즉, 절대 에러가 발생하지 않기 때문에, 자신의 코드가 예상대로 작동한다는 잘못된 자신감을 심어준다.

파이썬에서는 예외 처리해준 예외를 제외한 에러가 발생시 어디서 발생했고 왜 발생했는지에 대한 정보를 알려주기 때문에, 예상치 못한 에러에 대한 디버깅을 할 수 있다. 당장은 에러가 안 나는 코드가 우선적이고 좋아 보일 수 있지만, 미래를 생각하면 에러를 겪더라도 더 나은 에러 핸들링이 적용된 코드가 당연히 더 좋다고 생각한다.

Reference