[Python] 예외 처리

미남로그·2021년 10월 21일
0

5장 정리

1️⃣ 클래스
2️⃣ 예외 처리
3️⃣ 내장 함수
4️⃣ 라이브러리
5️⃣ 연습문제

저는 위 교재를 참고하여 학습 중이며 교재를 바탕으로 내용을 정리하였습니다🐍💨

5장의 이름은 '파이썬 날개달기'라는 chapter인데요. 그래서 그런지 필수적인 중요한 개념, 어려운 개념이 모두 모여 있는 것 같아요.💢

전 클래스도 클래스지만! 예외 처리도 많이 생소하게 느껴지는 파트였습니다. 저 강의 영상만 한 다섯 번은 돌려 봤습니다.

머리로는 '아 그렇구나'하긴 해도 잘 안 쓰니까 금방 까먹고, 실전에선 어떻게 쓰이는지 궁금했습니다.🤔

일단 살펴보겠습니다.



1. 오류는 어떤 때 발생하는가?

오류를 처리하는 방법을 알기 전에 어떤 상황에서 오류가 발생하는지 알아야 할 것 같습니다.

❗ 디렉터리 안에 없는 파일을 열려고 시도했을 때 발생하는 오류

>>> f = open("나없는파일", 'r')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '나없는파일'
  • FileNotFoundError 오류 발생

❗ 0으로 다른 숫자를 나누는 경우

>>> 4 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
  • ZeroDivisionError 오류 발생

❗ 제가 제일 싫어하는 오류

>>> a = [1,2,3]
>>> a[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
  • IndexError 오류 발생

list 배열 안 맞는는 오류 뜨면 미쳐버릴 것 같음😭



2. 오류 예외 처리 기법

이제 이런 오류를 처리하는 기법을 알아 보겠습니다.

우리가 이번에 친해져야 할 친구들을 살펴보겠습니다.


(1) try, except문

try:
	...
except [발생 오류[as 오류 메시지 변수]]:
	...

try 블록에서 오류가 발생하면 except 블록에 수행됩니다. try 블록에 오류가 없다면 except 블록은 수행되지 않습니다.

except [발생 오류 [as 오류 메시지 변수]]:

위 구문을 보면 [ ] 기호를 사용하는데, 이 기호는 괄호 안의 내용을 생략할 수 있다는 관례 표기법입니다.

즉, except 구문은 다음 3가지 방법으로 사용할 수 있습니다.


1) try,except만 쓰는 방법

try:
	...
except:
	...

이 경우는 발생 오류명을 작성하지 않았습니다. 그래서 오류 종류에 상관없이 오류가 발생하면 except 블록을 수행합니다.


2) 발생 오류만 포함한 except문

try:
	...
except 발생 오류:
	...

이 경우는 오류가 발생했을 때 except문에 미리 정해 놓은 오류 이름과 일치할 때만 except 블록을 수행합니다.


3) 발생 오류와 오류 메시지 변수까지 포함한 except문

try
	...
except 발생 오류 as 오류 메시지 변수:
	...

이 경우는 두 번째 경우에서 오류 메시지의 내용까지 알고 싶을 때 사용하는 방법입니다.

이 방법의 예시는

try:
	4 / 0
except ZeroDivisionError as e:
	print(e)

위처럼 4를 0으로 나누려고 하면 ZeroDivisionError가 발생하여 except 블록이 실행되고 변수 e에 담기는 오류 메시지를 다음과 같이 출력합니다.

결과값: division by zero



(2) try .. finally

try문에는 finally절을 사용할 수 있습니다.

finally절은 try문 수행 도중 예외 발생 여부에 상관없이 항상 수행된다.

보통 finally절은 사용한 리소스를 close해야 할 때에 많이 사용됩니다.

f = open('foo.txt', 'w')
try:
    # 무언가를 수행한다.
finally:
    f.close()

foo.txt 파일을 쓰기 모드로 연 후에 try문을 수행한 후 예외 발생 여부와 상관없이 finally절에서 f.close()로 열린 파일을 닫을 수 있습니다.



(3) 여러개의 오류처리하기

try문 안에서 여러 개의 오류를 처리하고 싶을 수 있습니다. 그럴 때는 if문처럼 elif를 여러 개 쓰는 방식처럼 여러 개의 오류를 처리할 수 있는데요.

그러기 위해 다음 구문을 사용합니다.

try:
    ...
except 발생 오류1:
   ... 
except 발생 오류2:
   ...

except를 여러 번 사용해주면 됩니다!

try:
    a = [1,2]
    print(a[3])
    4/0
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")
except IndexError:
    print("인덱싱 할 수 없습니다.")

해당 예시에서는 발생하는 두 가지 에러를 except문을 두 번 써서 처리해줍니다.

  1. ZeroDivisionError
  2. IndexError

여기서 오류 메시지를 가져오고 싶으니까 수정을 해보겠습니다.

try:
    a = [1,2]
    print(a[3])
    4/0
except ZeroDivisionError as e:
    print(e)
except IndexError as e:
    print(e)

그러면 오류 메시지가 출력이 되겠군요.

근데 저는 except를 여러 번 써야만 하는 줄 알았더니 한 번에 처리도 가능하네요😳

try:
    a = [1,2]
    print(a[3])
    4/0
except (ZeroDivisionError, IndexError) as e:
    print(e)

except (ZeroDivisionError, IndexError) as e:

이렇게 오류를 동일하게 처리하기 위해서 괄호를 사용하여 함께 묶어서 처리했습니다.



+) try문에 else 사용하기

try문에는 else가 사용이 가능하다고 합니다.😳 복잡하죠? .. 전 아직도 헷갈려요.

try:
	...
except [발생 오류 [as 오류 메시지 변수]]:
	...
else # 오류가 없을 경우에만 수행됨
	...

try문 수행중 오류가 발생하면 except절이 수행되고 오류가 없으면 else절이 수행됩니다.

다음은 try문에 else절을 활용하는 간단한 예입니다.

try:
    age=int(input('나이를 입력하세요: '))
except:
    print('입력이 정확하지 않습니다.')
else:
    if age <= 18:
        print('미성년자는 출입금지입니다.')
    else:
        print('환영합니다.')

만약 '나이를 입력하세요:' 라는 질문에 숫자가 아닌 다른 값을 입력하면 오류가 발생할 것입니다.

그러면 '입력이 정확하지 않습니다.'라는 문장을 출력합니다.

오류가 없을 경우에만 else절이 수행됩니다. else절에는 if-else절이 또 들어가 있는 형태네요. 이제 이런 거에 더 헷갈리지 않습니다...😳

  1. try-except-else 구문
  2. input에 int 값(try)이 아니면 오류 출력(except)
  3. 입력값 오류가 없으면 else절 수행
  4. 입력된 값이 18보다 적으면... (if-else문 수행)


3. 오류 회피하기

회피하고 싶은 순간이 늘 많죠...

프로그래밍에서도 특정 오류가 발생할 경우 그냥 pass 시켜야 할 때가 있다고 합니다.

try:
	f = open("나 없는 파일이야.", "r")
except FileNotFoundError:
	pass

try문 안에서 FileNotFoundError가 발생할 경우에 pass를 사용하여 오류를 회피하도록 작성한 예제입니다!



4. 오류 일부러 발생시키기

뭘 또 일부러까지... 싶겠지만 일부러 발생시켜야 할 경우도 생긴데요! 도대체 뭔 경우일까요?

여기서 새로운 친구를 또 만납니다. 이게 가장 생소하게 느껴졌습니다.

raise 명령어를 사용해 오류를 강제로 발생시킬 수 있습니다.

예를 들어 bird라는 클래스를 상속받는 자식 클래스는 반드시 fly라는 메서드를 구현하도록 만들고 싶은 경우가 있다면

class Bird:
	def fly(self):
		raise NotImplementedError

※ NotImplementedError는 파이썬 내장 오류로, 꼭 작성해야 하는 부분이 구현되지 않았을 경우 일부러 오류를 일으키기 위해 사용한다.

class Eagle(Bird):
    pass

eagle = Eagle()
eagle.fly()

Eagle 클래스는 Bird 클래스를 상속받습니다. 그런데 Eagle 클래스에서 fly 함수를 구현하지 않았기 때문에, Bird 클래스의 fly 함수가 호출됩니다. 그리고 raise문에 의해 NotImplemented Error가 발생하게 됩니다.

※ 상속받는 클래스에서 함수를 재구현하는 것을 메서드 오버라이딩이라고 부른다.

# 발생 오류 확인하기
Traceback (most recent call last):
  File "...", line 33, in <module>
    eagle.fly()
  File "...", line 26, in fly
    raise NotImplementedError
NotImplementedError

NotImplementedError가 발생되지 않게 하려면 다음과 같이 Eagle 클래스에 fly 함수를 반드시 구현해야 합니다.

class Eagle(Bird):
    def fly(self):
        print("very fast")

eagle = Eagle()
eagle.fly()

이제 fly 함수를 구현하였으니 오류 없이 very fast가 출력됩니다.



5. 예외 만들기

프로그램 수행 도중 특수한 경우에만 예외 처리를 하기 위해서 종종 예외를 만들어서 사용합니다.

예외는 다음과 같이 파이썬 내장 클래스인 Exception 클래스를 상속하여 만들 수 있다.

class MyError(Exception):
    pass

그리고 클래스 할 때도 만들었던 함수를 또 만들어보겠습니다.

def say_nick(nick):
	if nick == '바보':
    		raise MyError()
        print(nick)

그리고 함수를 호출해보겠습니다.

say_nick('천사')
say_nick('바보')

저장한 뒤 프로그램을 실행해 보면 다음과 같이 "천사"가 한 번 출력된 후 MyError가 발생한다.

천사
Traceback (most recent call last):
  File "...", line 11, in <module>
    say_nick("바보")
  File "...", line 7, in say_nick
    raise MyError()
__main__.MyError

이번에는 예외 처리 기법을 사용하여 MyError 발생을 예외 처리해 보겠습니다.

try:
	say_nick('천사')
    say_nick('바보')
except MyError:
	print("허용되지 않는 별명입니다.")

이번에 다시 실행해보면 오류 발생 대신

천사
허용되지 않는 별명입니다.

이라 뜹니다.

만약 오류 메시지를 출력하도록 또 변경하고 싶다면?

try:
	say_nick('천사')
    say_nick('바보')
except MyError as e:
	print(e)

그런데 또 이렇게 실행하면 print(e)로 오류 메시지가 출력되지 않습니다.

그 이유는 오류 메시지를 출력했을 때 오류 메시지가 보이게 하려면 class에 다음과 같은 __str__ 메서드르 구현해야 한다고 합니다.

__str__ 메서드는 print(e)처럼 오류 메시지를 print문으로 출력할 경우 호출되는 메서드입니다.

원래 사용했던 class는

class MyError(Exception):
    pass

이거였는데요.

class MyError(Exception):
    def __str__(self):
    	return "허용되지 않는 별명입니다."

이렇게 클래스를 수정해줘야 한다고 합니다!

어렵네요😳 갠적으로 무진장 어렵습니다...! 다시 복습하는 시간을 가져야겠어요. 파이썬 공부하는 모두 파이팅🐍

profile
미남이 귀엽죠

0개의 댓글