본문 바로가기

파이썬 튜터리얼 정리 8 에러와 예외 エラーと例外

https://docs.python.org/ko/3/tutorial/errors.html

https://psklog.tistory.com/54?category=788419

https://wikidocs.net/30


8. 에러와 예외 エラーと例外 - (4문)



8.1. 문법 에러 構文エラー


문법에러 = 파싱에러 (parsing error)



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


SyntaxError (문법 에러) 

문제 줄(line 1)  + 화살표로 표시

-> True : print('Hello world')



* 자주 발생하는 오류 


1) FileNotFoundError

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

2) ZeroDivisionError

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

3) IndexError

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


+) ZeroDivisionError, NameError, TypeError

>>> 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't convert 'int' object to str implicitly

- 에러메시지 앞 부분 : 스택 트레이스 형태 / 에러가 일어난 위치 표시 (표준입력에서 읽어들인 줄들은 표시x)

- 마지막 줄 : 에러 종류

- 줄의 나머지 부분 : 예외의 형과 원인


8.2. 예외 例外


실행중에 감지되는 에러들을 '예외'라고 하며 무조건 치명적이지는 않다.

例外 (exception) と呼ばれ、常に致命的とは限りません


ZeroDivisionError, NameError, TypeError... : 내장 예외 명 ( 내장식별자. 예약키워드 x)

標準例外の名前は組み込みの識別子です (予約語ではありません)。



8.3. 예외 처리하기 例外を処理する


예외를 선택하여 처리할 수 있다.

1) try, except문

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

try 문에서 '에러'가 발생하면 except 문 실행. 




except 구문 3가지

1. try, except만 쓰는 방법

try:
    ...
except:
    ...

오류 종류에 상관없이 오류가 발생하기만 하면 except 블록을 수행한다.

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

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

 오류가 발생했을 때 except문에 미리 정해 놓은 오류 이름과 일치할 때만 수행

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

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

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

ex)

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

 ZeroDivisionError가 발생. e라는 오류 메시지를 다음과 같이 출력한다.

division by zero



2) try .. finally

 finally절은 예외상관없이 항상 수행 - 보통 사용한 리소스를 close해야 할 경우에 많이 사용한다.

ex)

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

foo.txt 파일을 쓰기 모드로 연 후, try 수행 다음 finally절 에서 f.close()로 열린 파일을 닫을 수 있음.


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

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

ex)

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

IndexError "인덱싱 할 수 없습니다."라는 문자열이 출력

- 인덱싱 오류가 먼저 발생했으므로 4/0으로 발생되는 ZeroDivisionError는 발생 x


ex) as e :

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

- "list index out of range"라는 오류 메시지 출력

 ex) 오류 함께 처리하기 except (..., ...) :  

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

- 괄호로 묶어주면 함께 처리된다.


8.4. 예외 일으키기 例外を送出する

 : 프로그래밍을 하다 보면 종종 오류를 일부러 일으켜야 할 경우도 생긴다.



명령어 'raise' : 오류를 강제로 발생


ex)

class Bird:
    def fly(self):
        raise NotImplementedError

NotImplementedError 내장 오류, 꼭 작성해야 하는 부분이 구현되지 않았을 경우 오류 발생


class Eagle(Bird):
    pass

eagle = Eagle()
eagle.fly()

- Eagle 클래스는 Bird 클래스를 상속받는다.

- 그런데 Eagle에서 fly 를 재구현하지 않았기 때문에 Bird의 fly가 호출  (메소드 오버라이딩x)

 그러면 raise문에 의해 다음과 같은 NotImplementedError가 발생


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

내장 글래스 'Exception


: 특수한 경우에만 예외처리를 하기 위해 Exception을 이용해 예외를 만들 수 있다.

class MyError(Exception):
    pass


ex) 별명함수 say_nick

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


- say_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("허용되지 않는 별명입니다.")

- 결과 : 

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

- if) 오류메시지를 이용하고 싶다면 다음처럼 예외처리를 해야 할 것이다.

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


- 하지만 실행 해 보면 print(e)로 출력한 오류메시지가 아무것도 출력되지 않는것을 확인 할 수 있다. 

오류 메시지를 출력했을 때 오류 메시지가 보이게 하기 위해서는 오류 클래스에 다음과 같은 __str__ 메써드를 구현해야 한다.

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

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

다시 실행해 보면 "허용되지 않는 별명입니다."라는 오류메시지가 출력되는 것을 확인할 수 있다.



---------------------

[문제1] 예외처리

다음 코드의 실행결과를 예측하고 그 이유에 대해서 설명하시오.

result = 0
try:
    [1, 2, 3][3]
    "a"+1
    4 / 0
except TypeError:
    result += 1
except ZeroDivisionError:
    result += 2
except IndexError:
    result += 3
finally:
    result += 4

print(result)

-     [1, 2, 3][3] : IndexError -> result = 3

-   finally: result += 4    -> result = 3+4 = 7   

-     print(result)     -> 7












--------------------------------

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


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


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")


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


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()


>>> 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


>>> 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


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


raise ValueError  # shorthand for 'raise ValueError()'


>>> 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


class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message


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


>>> 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'


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


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