본문 바로가기
Programming/Python

16. 구문 오류와 예외

by IT learning 2021. 3. 21.
728x90

오늘은 코딩을 하는 도중 항상 만나게 되는 구문오류와 예외에 대해 설명해보겠다.

 

오류의 종류

프로그래밍 언어의 오류(Error)에는 크게 두 가지가 존재한다.

  • 프로그램 실행 전에 발생하는 오류
  • 프로그램 실행 중에 발생하는 오류

두가지 모두 '오류'라고 부른다.

실행전에 발생하는 오류를 구문 오류(SyntaxError)라고 부르고, 프로그램 실행 중에 발생하는 오류를 예외(Exception)라고 부른다.

 

구문 오류

구문 오류는 괄호의 개수, 들여쓰기 문제 등 프로그램이 실행도 되기 전에 발생하는 오류이다.

코딩을 할때 문법적으로 맞지 않거나, 이름의 선언등이 문제라고 할 수 있다.

print("안녕하세요)

위 출력문을 보자.

코드는 print 함수에 문자열 쌍따옴표가 다 채워지지 않은 상태이다. 이 경우에 발생하는 오류문구는 다음과 같다.

SyntaxError: EOL while scanning string literal

코드를 실행하면 EOL(End of Line)에 문제가 있다고 한다.

중요한 것은 앞에 적혀 있는 SynatxError라는 단어이다. 구문에 문제가 있어 애초에 실행도 안된 것 이다.

따라서 코드를 제대로 수정해야 프로그램이 실행이 된다.

 

예외

예외 또는 런타임 오류실행 중에 발생하는 오류를 의미한다.

print("프로그램이 실행되었습니다.")

list_a[1]

 

프로그램이 실행되었습니다. 라는 문자열이 출력이 된다.

NameError: name 'list_a' is not defined

그러니까 일단 프로그램이 실행되었다는 의미이다. 그런데 list_a[1]을 읽을 때 NameError가 발생한다.

이처럼 프로그램이 일단 실행된 다음, 실행 중에 발생하는 오류를 예외 또는 런타임 오류라고 부른다.

 

위 오류는 list_a라는 이름을 가진 것이 정의가 되지 않았다라는 의미이니, list_a 라는 이름을 가진 것을 만들어주면 된다.

print("프로그램이 실행되었습니다.")

list_a = [1,2,3,4]
list_a[1]

이렇게 말이다!

 

기본 예외 처리

예외를 해결하는 모든 것을 예외 처리(Exception Handling)라고 부른다.

예외를 처리하는 방법은 두가지로 나뉜다.

  • 조건문을 사용하는 방법
  • try 구문을 사용하는 방법

조건문으로 예외 처리하기

user_input_a = input("정수 입력 > ")

if user_input_a.isdigit() :
    number_input_a = int(user_input_a)
    print("원의 반지름 : ", number_input_a)
    print("원의 둘레 : ", 2 * 3.14 * number_input_a )
    print("원의 넓이 : ", 3.14 * number_input_a * number_input_a)
else :
    print("정수를 입력하지 않았습니다.")

위 코드를 보자.

문자열의 isdigit() 함수를 사용해서 숫자로만 구성된 글자인지 확인한다.

그리고 그게 맞다면 반지름, 둘레, 넓이를 구해 출력하고, 아닐경우 정수를 입력하지 않았다는 문구를 내보낸다.

위 코드의 실행결과이다.

위와 같이 숫자를 입력했을경우 해당하는 결과값을 출력, 아닐경우 아닐때의 해당하는 결과값을 출력하게 된다.

 

try except 구문

요즘 프로그래밍 언어는 예외를 처리할 수 있는 구문을 제공한다.

그것이 바로 try except 구문이다.

try:
    number_input_a = int(input("정수 입력 > "))
    print("원의 반지름 :", number_input_a)
    print("원의 둘레 : {:.3f}".format(2 * 3.14 * number_input_a))
    print("원의 넓이 :", 3.14 * number_input_a * number_input_a)
except:
    print("무언가 잘못되었습니다.")

위 코드를 보자.

예외가 발생할 수 있는 코드를 try 구문에 넣고, 예외가 발생했을 때 실행할 코드를 except 구문에 넣었다.

구동은 위 코드와 동일하다.

 

try except 구문과 pass 키워드 조합하기

예외가 발생하면 일단 처리는 해야 하지만,

해당 코드가 딱히 중요한 부분이 아니라면 일단 프로그램이 강제 종료되는것부터 막자는 목적으로 except 구문에 아무것도 넣지 않는다.

하지만 아무것도 넣지 않으면 구문 오류가 발생하므로 pass 키워드를 넣어준다.

list_input_a = ["52", "273", "32", "스파이", "103"]

list_number = []
for item in list_input_a :
    try:
        float(item)
        list_number.append(item)
    except:
        pass
    
print("{} 내부에 있는 숫자는".format(list_input_a))
print("{} 입니다".format(list_number))

위 코드는 pass 키워드를 활용한 코드이다.

코드는 list_input_a 에 숫자만 list_number에 추가하는 코드이다.

반복문을 돌며 하나하나 뽑아서 try 구문으로 돌린다.

숫자가 아닐 경우엔 pass 키워드로 넘어간다.

위 코드의 실행결과이다.

실행결과 '스파이' 문자열을 제외하고 모든 숫자들이 추가가 됐다.

 

try except else 구문

try except 구문에 else 구문을 붙여서 사용하면 예외가 발생하지 않았을 때 실행할 코드를 지정할 수 있다.

try:
    number_input_a = int(input("정수 입력 > "))
except:
    print("정수를 입력하지 않았습니다.")
else:
    print("원의 반지름:", number_input_a)
    print("원의 둘레: {:.2f}".format(2*3.14*number_input_a))
    print("원의 넓이:", 3.14 * number_input_a * number_input_a)

위 코드를 보자.

else는 사실 별 의미가 없다. 그냥 안써도 잘 돌아가게 할 수 있다. 그저 코드를 깔끔하게 쓸 수 있다라는 장점뿐이다.

하지만, 배우지 않아도 되는건 아니다. 다른 사람이 이 구문을 사용했다면 그게 어떤 역할을 하고 있는지는 알아야 하지 않겠는가?

세상에 배우지 않아도 되는 것은 없다.

 

finally 구문

finally 구문은 예외 처리 구문에서 가장 마지막에 사용할 수 있는 구문이다.

예외가 발생하든 예외가 발생하지 않든 무조건 실행할 때 사용하는 코드이다.

try:
    number_input_a = int(input("정수입력 > "))
    print("원의 반지름 :", number_input_a)
    print("원의 둘레 : {}".format(2*3.14*number_input_a))
    print("원의 넓이 :", 3.14 * number_input_a * number_input_a)
except:
     print("정수를 입력하지 않았습니다. 제대로 입력좀 해보세요.")
else:
     print("예외가 발생하지 않았습니다.")
finally:
    print("예..뭐..어떻게든 프로그램이 끝났습니다.")

위 코드를 보자.

코드 마지막에 finally 가 무조건 실행되는 코드이다.

위 코드의 실행결과이다.

정수를 입력하든, 입력하지 않든 finally 구문은 무조건 실행된다.

 

try, except, finally 구문의 조합

예외 처리 구문은 다음과 같은 규칙을 지켜야 한다.

  • try 구문은 단독으로 사용할 수 없으며, 반드시 except 구문 또는 finally 구문과 함께 사용해야 한다.
  • else 구문은 반드시 except구문 뒤에 사용해야 한다.

finally에 대한 오해

일반적으로 finally 키워드를 설명하면 예제로 '파일 처리'를 많이 사용한다.

하지만, 실제로 finally 구문을 사용하는 것과는 전혀 관련이 없다.

try:
    file = open("info.txt", "w")
    # 여러 가지 처리를 수행한다.
    예외.발생해라ㅋㅋ()
    # 파일을 닫습니다
    file.close()
except Exception as e:
    print(e)
    
print("# 파일이 제대로 닫혔는지 확인하기")
print("file.closed:", file.closed)

위 코드를 살펴보자.

위 코드는 try 구문에서 예외가 발생하여 except로 빠져나가고 파일이 닫히지 않는상태이다.

file.closed 기능을 사용하면 파일이 정상적으로 닫혔는지 여부를 확인할 수 있다.

위 코드의 실행결과이다.

결과를 보면, 예외가 발생해 중간에 끝났으므로 파일이 닫히지 않은채 끝이 났다.

try:
    file = open("info.txt", "w")
    # 여러 가지 처리 수행
    예외.발생()
    file.closed()
except Exception as e:
    print(e)
finally:
    file.close()
    
print("# 파일이 제대로 닫혔는지 확인하기")
print("file.closed:", file.closed)

그래서 finally 구문을 사용해 무조건 파일이 닫히게 설정하면 된다.

위 코드의 실행결과이다.

근데,

# try except 구문 끝난 후 파일 닫기
try:
    file = open("info.txt", "w")
    예외.발생()
except Exception as e:
    print(e)
    
file.close()

print("# 파일이 제대로 닫혔는지 확인하기")
print("file.closed :", file.closed)

# 사실 그냥 닫기만 하면 된다.
# finally 를 무조건 사용해야 된다는 건 말도 안되는 이야기였다.

위 코드의 실행결과이다.

그냥 finally 말고 try 구문 종료 후에 파일을 닫으면 되는거 아닌가? 

맞다. 다른게 없다. 한마디로 파일 처리를 할 때 무조건 finally 키워드를 사용해야 한다는 것은 말도 안되는 이야기란 소리다.

finally 키워드는 이 키워드를 사용하면 코드가 깔끔해질 것 같다고 생각되는 경우에 사용한다.

 

try 구문 내부에서 return 키워드를 사용하는 경우

finally 구문은 반복문 또는 함수 내부에 있을 때 위력을 발휘한다.

def test():
    print("test() 함수의 첫 줄입니다.")
    try:
        print("try 구문이 실행되었습니다.")
        return
        print("try 구문의 return 키워드 뒤입니다")
    except:
        print("except 구문이 실행되었습니다.")
    else:
        print("else 구문이 실행되었습니다.")
    finally:
        print("finally 구문이 실행되었습니다.")
    print("test() 함수의 마지막 줄입니다.")
    
test()

위 코드의 실행결과이다.

try 구문 내부에 return 키워드가 있다는 것이 포인트 이다. try 구문 중간에서 탈출해도 finally 구문은 무조건 실행된다.

함수 내부에서 파일 처리 코드를 깔끔하게 만들고 싶을 때 finally 구문을 활용하는 경우가 많다.

 

def write_text_file(file_name, text):
    try:
        file = open(file_name, "w")
        return
        file.write(text)
    except Exception as e:
        print(e)
    finally:
        file.close()
        
        
write_text_file("text.txt", "안녕하세요")

위 코드를 살펴보자.

만약 중간에 return 키워드 등으로 함수를 빠져나갈 때마다 close() 함수를 사용하면 코드가 복잡해졌을 것이다.

하지만 finally 구문에 close() 함수를 사용해서 무조건 쓰게끔 만들어 코드가 깔끔해지게 작성했다.

 

반복문과 함께 사용하는 경우

print("프로그램이 시작되었습니다.")

while True:
    try:
        print("try 구문이 시작되었습니다.")
        break;
        print("try 구문의 break 키워드 뒤입니다.")
    except:
        print("except 구문이 실행되었습니다.")
    finally:
        print("finally 구문이 실행되었습니다.")
    print("while 반복문의 마지막 줄입니다.")

print("프로그램이 종료되었습니다.")

finally 구문은 무조건 실행된다. 따라서 반복문에서 break 로 빠져나갈 때도 마찬가지로 무조건 실행이 된다.

위 코드의 실행결과이다.

 

정리
구문 오류
는 프로그램의 문법적인 오류로 프로그램이 실행조차 되지 않게 만드는 오류이다.
예외(런타임 에러)는 프로그램 실행 중에 발생하는 오류이다.
기본 예외 처리는 조건문 등을 사용해 예외를 처리하는 기본적인 방법이다.
try except 구문은 예외 처리에 특화된 구문이다.
728x90

'Programming > Python' 카테고리의 다른 글

18. 표준 모듈  (0) 2021.03.23
17. 예외 고급  (0) 2021.03.22
15. 함수 고급(파일 처리, 제네레이터)  (0) 2021.03.20
14. 함수 고급(람다, 튜플)  (0) 2021.03.20
13. 파이썬 - 함수의 활용  (0) 2021.03.19

댓글

IT_learning's Commit