스터디/파이썬 스터디 강의자료

[3팀/김경은] 10주차 파이썬 스터디 - 예외처리

경은 2023. 6. 2. 08:48

데이터 과학을 위한 파이썬 프로그래밍 교재를 사용하여 작성한 강의자료입니다.

10차시_예외처리_강의안.pdf
0.69MB
10차시_예외처리_과제.pdf
1.42MB

 

예외의 개념과 사례

  • 실제 프로그램을 만들기 위해 알아야 하는 예외 처리
  • 예외 (exception)란 프로그램을 개발하면서 예상하지 못한 상황이 발생하는 것이다.
  • 대표적인 사례로 사용자의 입력 오류가 있다. 예를 들어, 새로운 웹 사이트에 가입하여 아이디를 생성하려고 한다. 일반적으로 아이디는 영문자나 숫자를 지원하는데, 때로는 사용자가 한글로 아이디를 만들기 위해 한글 입력을 시도할 때가 있다. 이 경우 입력된 한글이 아무런 문제 없이 데이터베이스에 저장되면 자칫 사용자가 해당 웹 사이트에 로그인하지 못할 수도 있다. 프로그래머의 실수로 인코딩에 문제가 발생하여 데이터베이스에 한글이 잘못 저장될 수 도 있기 때문이다. 따라서 사용자가 이러한 실수를 하지 않도록 막는 것이 중요하다.
  • 또 다른 사례로 MS 오피스에서 지원하는 자동 저장(autosave) 기능이 있다. 이 기능은 MS 오피스 프로그램으로 작업하던 중 정전 등의 이유로 갑자기 컴퓨터가 종료되었을 때를 대비하여 작업한 것을 중간에 자동으로 저장해주는 기능이다. 자동 저장 기능은 게임이나 은행 거래 등 에서 매우 중요한 역할을 하고 있다.
  • 이러한 사례처럼 프로그램을 실제로 작성하고 실행하다 보면 예상하지 못한 일이 많이 발생 하는데, 이를 예외라고 한다.

 

예측 가능한 예외와 예측 불가능한 예외

예측 가능한 예외

  • 예측 가능한 예외는 발생 여부를 개발자가 사전에 인지할 수 있는 예외이다.
  • 예를 들어 ‘텍스트 박스에 사용자가 실수로 잘못된 값을 입력할 것 같다.’라든가 ‘사용자가 실제로 존재하지 않는 파일에 저장할 것 같다.’ 등은 개발자가 사전에 예측할 수 있는 것으로 얼마든지 예외 처리가 가능하다.
  • 즉, 개발자는 예외를 예측하여 명시적으로 예외가 발생할 때는 어떻게 대응하라고 대책을 마련해 놓을 수 있다.
  • 대표적으로 사용자 입력란에 값이 잘못 입력되었다면 if문을 사용하여 잘못 입력하였다고 응답하는 방법이 있다. 아주 쉽게 대응이 가능하다.

예측 불가능한 예외

  • 대표적으로 매우 많은 파일을 처리할 때 문제가 발생할 수 있다.
  • 예를 들어 5,000개의 파일이 한 디렉터리에 있고 그 파일명들을 넘겨 받아 파일을 하나씩 읽어들이는 프로그램을 만든다고 하자. 그런데 파일을 읽다가 어떤 파일은 실제로 안에 내용이 없을 수 있는데, 이런 경우에도 어떤 처리를 하지 못하고 넘어간다. 또는 파일을 읽고 있는데 다른 사용자에 의해 해당 파일이 삭제되어 읽어들이는 시점에 해당 파일이 사라지는 경우도 발생할 수 있다. 이외에도 인덱스의 범위를 넘어가는 리스트의 호출이나 정수를 0으로 나누는 것 등에 대한 문제가 생길 수 있다.
  • 이러한 예측 불가능한 예외가 발생했을 경우 인터프리터가 자동으로 이것이 예외라고 사용자에게 알려준다. 대부분은 이러한 예외가 발생하면서 프로그램이 종료되므로 적절한 조치를 해야 한다. 단순히 파일이 없다고 알려주는 것이 아니라 파일이 없기 때문에 추가로 해야 하는 조치가 있다면 그것을 예외 처리 구문에 추가해야 한다. 예를 들어, 처리해야 할 파일이 없다 면 파일이 없다고 기록을 남기고 다음 파일을 처리하는 방식으로 프로그램을 작성해야 한다.
  • 예외 처리가 중요한 이유는 프로그램도 일종의 제품이기 때문이다. 우리가 제품을 사용할 때 상세한 설명이 없거나 사용자 친화적이지 않은 경우 매우 화가 날 수 있다. TV 리모컨을 조금 만 잘못 눌러도 TV가 꺼진다면 사용자는 그 회사 제품을 더는 사용하지 않을 것이다. 이러한 이유로 예외 처리는 제품의 완성도를 높이는 차원에서 매우 중요한 개념이다.

예외 처리 구문

  • 예외 처리 구문을 파이썬으로 구현하는 방법

try-except문

try:
    예외 발생 가능 코드 
except 예외 타입:
    예외 발생 시 실행되는 코드
  • 파이썬에서 예외 처리의 기본 문법은 try-except문이다.
  • 사실 대부분의 프로그래밍 언어에서 예외를 처리하는 구문은 이와 비슷하다. 작성하는 방식은 try문에 예외 발생이 예상되는 코드를 적고, except문에 예외 발생 시 대응하는 코드를 작성한다.
for i in range(10): 
    try:
        print(10 / i)
    except ZeroDivisionError:
        print("Not divided by 0")
Not divided by 0 
10.0
5.0
3.3333333333333335 
2.5
2.0 
1.6666666666666667 
1.4285714285714286 
1.25 
1.1111111111111112
  • 위의 코드는 0부터 9까지의 숫자를 i 에 하나씩 할당 하면서 10 으로 나눈 값을 출력하는 코드이다.
  • 이 프로그램은 1이 아닌 0부터 시작하다 보니 10을 0으 로 나누는 계산이 가장 먼저 실행된다. 처음에 ‘10三0(10/0)’을 계산하면 0으로는 10을 나눌 수 없으므로 예외가 발생한다.
  • 하지만 이러한 예외의 발생은 예상 가능하므로 try문으로 해당 예외의 발생을 대비할 수 있다. ZeroDivisionError, 즉 0으로 나눠지는 경우에는 except문 안으로 들어가 해당 구문을 처리한다. 여기서는 print("Not divided by 0") 코드 가 실행된다.
  • 그런데 만약 여기서 try문이 for문 밖에 있으면 반복문 전체가 종료된다. 즉, try문 내부에서 예외가 발생하면 except문 영역에 있는 코드가 실행되고 try- except문이 종료된다. 이렇게 try문을 적당한 곳에 삽입하여 예외 처리를 할수 있다.

[참고] 예외의 종류와 예외에 대한 에러 메세지

  1. 예외의 종류
    • 파이썬에서 기본적으로 사용할 수 있는 예외의 종류는 다양하다.
    • 먼저, IndexError는 리스트의 인덱스 범위를 넘어갈 때 처리하는 예외이다. 코드를 작성하다 데이터가 150개인 줄 알았는데, 151 개가 있어 처리를 요청하는 경우 IndexError가 호출된다.
    • 다음으로 NameError는 코드에서 호출하는 특별한 변수명이 없을 때 호출되는 에러이다. 이 언어가 인터프리터 언어이다 보니 존재하지 않은 변수도 실행 시점에서 잡아낸다 컴파일러 언어의 경우 실행 전이나 컴파일러 시점에 에러가 발생하는 것과는 대조적이다.
    • 그 외에 파이썬에서 자주 사용하는 내장 예외는 아래와 같다.
    예외 내용
    IndexError 리스트의 인덱스 범위를 넘어갈 때
    NameError 존재하지 않는 변수를 호출할 때
    ZeroDivisionError 0으로 숫자를 나눌 때
    ValueError 변환할 수 없는 문자나 숫자를 변환할 때
    FileNotFoundError 존재하지 않는 파일을 호출할 때
  2. 에러에 대한 에러 메세지
  • 내장 예외와 함께 사용하기 좋은 것이 예외에 대한 에러 메시지이다.
  • 아래 코드와 같이 except문의 마지막에 ‘as e’ 또는 ‘as 변수명’을 입력하고, 해당 변수명을 출력하면 된다. 실행 결과 'division by zero’라는 에러 메시지를 확인할 수 있는데, 이 에러메시지는 파이썬 개발자들이 사전에 정의한 것으로, 특정한 에러를 빠르게 이해할 수 있도록 해준다.
for i in range(10):
    try:
        print(10 / i)
    except ZeroDivisionError as e:
        print(e)
        print("Not divided by 0")
division by zero 
Not divided by 0 
10.0
5.0 
3.3333333333333335 
2.5
2.0 
1.6666666666666667 
1.4285714285714286 
1.25 
1.1111111111111112

try-except-else 문

  • try-except-else문은 if-else 문과 비슷한데, 해당 예외가 발생하지 않는 경우 수행할 코드를 else문에 작성하면 된다.
#try-except-else문의 기본 형태

try:
    예외 발생 가능 코드 
except 예외 타입:
    예외 발생 시 실행되는 코드
else:
    예외가 발생하지 않을 때 실행되는 코드

  • 위 코드는 10을 i로 나누는 코드를 실행하여 제대로 나누었을 경우 else문에 의한 결과가 화면에 출력되고, 그렇지 않을 경우 except문에 의해 사전에 정의된 에러가 발생하는 코드이다.
for i in range(10):
    try:
        result = 10 / i
    except ZeroDivisionError:
        print("Not divided by 0")
    else:
        print(10 / i)
Not divided by 0 
10.0
5.0
3.3333333333333335 
2.5
2.0 
1.6666666666666667 
1.4285714285714286 
1.25 
1.1111111111111112
  • 이 코드에서처럼 에러가 발생하지 않는 경우의 수행문을 정의해 놓으면 에러가 발생하지 않는 경우에도 일어날 일을 사용자가 정확히 예측할 수 있다는 장점이 있다. 하지만 일반적으로 많이 사용하지 않는 코드이며 예외가 많이 발생하는 상황에서 간혹 사용할 수 있는 코드이다. 예를 들어, 서버의 상태가 불안해 연결 끊김 (connection loss)이 자주 발생한다면 연결이 되었을 때 처리할 코드를 명확히 하는 것이 좋다.

try-except-finally문

  • try-except-finally문에서 finally문은 try—except문 안에 있는 수행 코드가 아무런 문제 없이 종료되었을 경우 최종으로 호출하는 코드이다. for문에서 사용하는 finally문과 용도가 비슷하다고 생각하면 된다
#try-except-finally문의 기본 형태

try:
    예외 발생 가능 코드 
except 예외 타입:
    예외 발생 시 실행되는 코드
finally:
    예외 발생 여부와 상관없이 실행되는 코드
  • try-except-finally문 코드
try:
    for i in ranged(1, 10):
        result =10 // i
        print(result)
except ZeroDivisionError:
    print("Not divided by 0")
finally:
    print("종료되었다.")
10 
5 
3 
2
2
1
1
1
1
종료되었다.
  • 이 코드는 try문이 for문 밖에 선언되어 있고, i 가 1부터 시작한다.
  • 사실상 ZeroDivision- Error가 발생할 수 없는 코드이다.
  • 이러한 코드를 작성하면 except문은 실행되지 않고 마지막으로 finally문만 실행된다. try-except-finally문도 for문에서 finally문을 사용하는 것과 동일하게 예외 발생 여부와 상관없이 반드시 실행되는 코드이다.

raise 문

  • raise문은 try—except문과 달리 필요할 때 예외를 발생시키는 코드이다.
  • while True문을 사용해 계속 반복문을 돌려야 하는 프로그램을 작성한다고 가정하자. 상대방 컴퓨터에 계속 무엇인가를 요청해야 하는 프로그램이고, 요청할 때마다 비용이 청구된다.
  • 만약 상대방 컴퓨터가 고장나거나 문제가 생겨 사용하지 못하게 되는 상황이 발생한다면 어떻게 해야 할까? 프로그램을 작성할 때 상대방 컴퓨터에서 응답이 없다면 강제로 프로그램이 종료되도록 예외를 발생시킬 수 있다. 다음은 raise문의 기본 형태이다.

💡 raise 예외 타입(예외 정보)

  • raise 문 코드
while True:
    value = input("변환할 정수값을 입력해 주세요: ") 
    for digit in value:
        if digit not in "0123456789":
        raise ValueError("숫자값을 입력하지 않았습니다.") 
    print("정수값으로 변환된 숫자-", int(value))
변환할 정수값을 입력해 주세요: 10 
정수값으로 변환된 숫자 -10
변환할 정수값을 입력해 주세요: ab 
Traceback (most recent call last):
    File "raise.py", line 5, in〈module〉
        raise ValueError("숫자값을 입력하지 않았습니다.")
ValueError: 숫자값을 입력하지 않았습니다.
  • 위 코드는 while True문으로 반복문이 계속 돌아가는 상태에서 사용자에게 입력을 받는다.
  • 하지만 사용자가 입력한 값이 숫자가 아닌 경우에는 숫자값을 입력하지 않았다고 출력하면서 프로그램을 종료하는 것을 목적으로 작성된 프로그램이다. 이때, 에러의 종류는 ValueError로 사용자가 입력을 잘못했을 때 입력이 잘못된 것을 알려주면서 종료한다.

assert 문

  • 미리 알아야 할 예외 정보가 조건을 만족하지 않을 경우 예외를 발생시키는 assert문
  • 앞에서 if문과 함께 raise문을 사용하여 강제로 예외를 발생시켰는데, assert문은 매우 간단하게 예외를 발생 시킬 수 있다. assert문은 True 또는 False의 반환이 가능한 함 수를 사용하면 된다. 만약 False가 반환되면 예외가 발생한다. 다음은 assert문의 기본 형태 이다.

💡 assert 예외 조건

  • assert 문 코드
def get_binary_number(decimal_number):
    assert isinstance(decimal_number, int)
    return bin(decimal_number)
print(get_binary_number(10))
print(get_binary_number("10"))
0b1010                                    #4행실행결과 
Traceback (most recent caU last):         #5행 실행 결과
    File "<C:/.../assert.py>", line 5, in <module> 
        print (get_binary_number("10"))
    File "C:/.../assert.py", line 2, in get_binary_number 
        assert isinstance(decimal_number, int)
AssertionError
  • 1행에서 get_binary_number() 함수를 통해 십진수를 입력받는다.
  • 하지만 함수를 사용하는 사용자가 잘못된 인수, 예를 들어 문자열값을 입력할 수도 있다. 이를 방지하기 위해 2행에서 assert문을 사용하였다.
  • isinstance() 함수는 입력된 값이 다음에 나오는 클래스의 인스턴스인지를 확인하는 함수이다. 이 코드에서 decimal_number 변수가 정수형 인지는 4〜 5행에서 확인할 수 있다.
  • assert문은 코드를 작성할 때 잘못된 입력 여부를 사전에 확인하여 나중에 필요 없는 연산을 막아주고, 결국 다른 사람이 만든 코드를 사용하는데 좋은 가이드가 된다.

파일의 개념

  • 파일(file)은 컴퓨터를 실행할 때 가장 기본이 되는 단위이다.
  • 프로그램을 실행하는 것은 늘 파일을 실행하는 것이다. 하지만 일반적으로 윈도의 GUI 환경에서는 파일을 실행하는 것이 아니라, 아래와 같은 아이콘을 더블클릭하여 실행한다.

사실 이것도 아이콘을 더블클릭하여 프로그램을 실행하는 것처럼 보이지만 실제로는 아이콘 과 연결된 파일이 실행되는 구조이다. 아이콘에서 마우스 오른쪽 버튼을 클릭하고 [속성]을 선택하면 아래와 같은 화면을 볼 수 있다.

  • 속성 화면에서 ‘대상’ 항목에 실행되는 파일의 경로가 있다. 이 경로를 그대로 복사하여 콘솔 창에 붙여 넣고 실행하면 해당 프로그램이 실행된다. 모든 프로그램은 이렇게 파일이 있기 때 문에 실행되는 것이다.
  • 단지 사용자가 편하게 쓰기 위해 간단한 아이콘을 만들어 사용할 뿐이지, 그 이면에는 모두 파일이 숨어 있다고 할 수 있다.

[참고]

  • 파일을 이해하기 위해 먼저 파일과 디렉터리에 대해 알아보자.
  • 윈도에서 사용하는 탐색기는 [윈도]와 [E]키를 함께 누르면 나타난다. 이것이 기본 파일 시스템이다. 기본적으로 파일 시스템은 파일과 디렉터리로 구분하는데, 윈도에서는 디렉터리라는 용어 대신 폴더라는 용어를 사용한다.
  • 디렉터리는 파일을 담는 또 하나의 파일로, 여러 파일을 포함할 수 있는 그릇이다.
  • 파일과 다른 디렉터리를 포함할 수 있으므로 직접 프로그램을 실행하지는 않지만 다른 파일들을 구분하고 논리적인 단위로 파일을 묶을 수 있다.
  • 파일은 컴퓨터에서 논리적으로 정보를 저장하는 가장 작은 단위이다. 파일은 일반적으로 파일명과 확장자로 구별 한다. 예를 들어, 파이썬 파일로 저장 관리하는 파일들은 py라는 확장자를 가지고 있다. 확장자는 그 파일의 쓰임을 구분하는 글자로, hwp, ppt, doc 같은 것들이 있다.
  • 파일은 다른 정보를 저장하거나 프로그램을 실행하고 다른 프로그램이 실행될 때 필요한 정보를 제공하는 등 여러 가지 역할을 한다. 흔히 탐색기 프로그램에서 파일과 디렉터리는 트리 구조로 표현되는데, 그 이유가 바로 디렉터리와 파일이 서로 포함 관계를 가지기 때문이다.

 

파일의 종류

  • 컴퓨터에서 파일의 종류는 다양하지만 기본적으로 바이너리 파일과 텍스트 파일 두 가지로 분류할 수 있다.
  • 바이너리 파일은 컴퓨터만 이해할 수 있는 이진 정보로 저장된 파일이다. 즉, 비트 형태로 저장되어 메모장으로 열면 내용이 보이지 않거나 내용을 확인할 수 없는 파일을 말한다. 정보 를 효율적으로 저장하기 위해 파일 시스템 대부분은 이진 정보로 저장된다. 엑셀이나 워드 프로그램들이 모두 이진 정보로 저장된 이진 파일이다.
  • 텍스트 파일은 사람이 이해할 수 있는 문자열로 저장된 파일이다. 이러한 파일은 메모장으로 그 내용을 확인할 수 있다. 지금까지 작성한 파이썬 파일이나 HTML 코드 파일 등도 모두 텍 스트 파일이다. 생각보다 많은 파일이 텍스트 파일로 저장되어 있다.

바이너리 파일 텍스트 파일

- 컴퓨터만이해할수있는형태인 이진(법)형식으로 저장 된 파일  
  • 일반적으로 메모장으로 열면 내용이 깨져 보임(메모장에서 해석 불가)
  • 엑셀이나 워드와 같은 프로그램 파일 | - 사람도 이해할 수 있는 형태인 문자열 형식 으로 저장된 파일, 메모장으로 열면 내용 확인이 가능
  • 메모장에 저장된 파일이나 HTML 파일, 파이썬 코드 파일등 |
  • 여기서 알아야 할 점은 텍스트 파일도 사실 컴퓨터가 처리하기 위해 바이너리 형태로 저장 된 파일이라는 것이다.
  • 사람이 확인할 수 있는 파일이라고 해서 컴퓨터가 그런 형태로 저장된 파일을 확인할 수 있는 것은 아니다. 텍스트파일은 컴퓨터가 이해할 수 있는 형태로 변경하여 저장된다.
  • 흔히 이렇게 변경하는 기준을 아스키= ASCII나 유니코드Unicode로 하고, 이 표준에 따라 텍스트 파일을 컴퓨터가 이해할 수 있도록 바꾼다. 즉, 컴퓨터는 오직 이진수만 이해할 수 있으므로 모든 문자열값도 전부 이진수로 변경하여 저장한다.
  • 예를 들어, ‘A라는 문자열값이 이진수로 0100010이다.’라는 표준에 따라 우리 눈에 A로 보이는 텍스트는 실제로 0100010으로 저장된다. 반대로 0100010으로 저장되어 있는 텍스트 파일을 불러올 때 표준에 따라 A로 다시 변경하여 보여준다.

[참고]

  • 모든 텍스트는 이진수로 변환되어 저장되므로 기본적인 변환 표준이 만들어졌고 그 표준을 사용하여 정보를 쉽게 저장할 수 있다. 이러한 표준은 아스키코드나 유니코드처럼 나라별, 언어별로 존재한다. 어떤 텍스트를 이러한 체계에 맞게 저장하는 것을 인코딩 (encoding)이라고 한다.

 

파일 읽기

  • 파이썬에서는 텍스트 파일을 다루기 위해 open() 함수를 사용한다.
  • 일반적으로 open() 함수와 파일명, 파일 열기 모드를 입력하면 그 옵션에 따라 파일을 다룰 수 있다.
f = open("파일명", "파일 열기 모드") 
f.close()
  • 파일을 여는 모드에는 읽기 모드, 쓰기 모드, 추가 모드 등이 있다. 파이썬에서는 상황에 따라 이러한 형식을 조합하여 파일을 다루게 된다.

종류 설명

r 읽기 모드:파일을 읽기만 할 때 사용
w 쓰기 모드:파일에 내용을 쓸 때 사용
a 추가 모드:파일의 마지막에 새로운 내용을 추가할 때 사용

파일 읽기

f = open ("dream.txt", "r")
contents = f.read()
print(contents) 
f.close()
I have a dream a song to sing
to help me cope with anything
if you see the wonder of a fairy tale 
you can take the future even
if you fail I believe in angels 
something good in everything
  • 1행에서 open() 함수 다음에 파일명과 ‘r’을 입력해 파일에 대한 정보를 변수 f에 저장하였다. 이를 일반적으로 파일 객체라고 한다.
  • 2행에서 변수 f를 이용해 read() 함수를 실행하면 해당 파일의 텍스트를 contents 변수에 문자열로 저장한다.
  • 3행에서는 contents 변수의 내용, 즉 ‘dream.txt’ 파일에 있는 텍스트를 화면에 출력한다.
  • 4행에서는 마지막으로 close() 함수를 사용하여 파일을 종료한다.
  • 때때로 텍스트 파일을 다룰 때 이미 수정하고 있는 파일을 다른 프로그램에서 동시에 호출하면 에러가 발생한다. 이렇게 하나의 파이썬 프로그램이 하나의 파일을 사용할 때 사용을 완료하면 반드시 해당 파일을 종료해야 한다.

with 문과 함께 사용하기

  • with문과 함께 open() 함수를 사용할 수 있다.
  • with문은 들여쓰기를 하는 동안에는 open() 함수가 유지되고, 들여쓰기가 끝나면 open() 함수도 종료되는 방식이다.
  • 차이점은 close() 함수를 명시적으로 쓰지 않아도 파일의 사용을 종료할 수 있다는 점이다. 특히 해당 파일 객체는 =로 할당하는 방식이 아니라 as문을 사용하여 변수명에 할당 한다.
with open("dream.txt", "r") as my_file:
    contents = my_file.read()
    print(type(contents), contents)
<class 'str'> I have a dream a song to sing 
to help me cope with anything
if you see the wonder of a fairy tale
you can take the future even
if you fail I believe in angels
something good in everything

한 줄씩 읽어 리스트형으로 반환하기

  • 파일 전체의 텍스트를 문자열로 반환하는 read() 함수 대신, readlines() 함수를 사용하여 한 줄씩 내용을 읽어와 문자열 형태로 저장할 수 있다.
  • 한 줄의 기준은 \n으로 나뉘어지고 리스트로 반환될 때 for문 등 다양한 형태로 활용할 수 있다.
with open("dream.txt","r") as my_file:
    content_list = my_file.readlines()   # 파일 전체를 리스트로 반환
    print (type(content_list))           # 자료형 확인
    print(content_list)                  # 리스트값 출력
<class 'list'〉
['I have a dream a song to sing \\n', 'to help me cope with anything \\n', 'if you 
see the wonder of a fairy tale \\n', 'you can take the future even \\n', 'if you 
fail. I believe in angels \\n', 'something good in everything \\n']

실행할 때마다 한 줄씩 읽어 오기

  • 실행할 때마다 차례대로 한 줄씩 읽어오는 함수인 readline()에 대해 알아보자.
  • 기존 read() 함수는 파일을 한 번 열 때 파일의 처음부터 끝까지 모든 파일의 내용을 읽어오는 함수이지만, readline() 함수는 호출될 때마다 한 줄씩 읽어오는 특징이 있다.
with openC'dream.txt", "r") as my_file:
    i=0
    while 1:
        line = my_file.readline()
        if not line:
            break
        print(str(i)+" === "+ line.replace("\\n","")) #한 줄씩 값 출력
        i=i+1
0 === I have a dream a song to sing
1 === to help me cope with anything
2 === if you see the wonder of a fairy tale
3 === you can take the future even
4 ===ifyoufailIbelieveinangels
5 === something good in everything
  • 코드를 보면 3행에서 while 1로 코드가 항상 수행되도록 만든 다음, 4행의 line = my_file.readline()으로 한 줄씩 파일을 읽어들인다.
  • 만약 읽어온 줄에 내용이 없다면 5행의 if not line: break 코드에 의해 반복문이 종료되어 파일 읽기를 종료한다. 하지만 파일에 남은 내용이 있다면 while문이 계속 실행되면서 모든 코드를 다 읽어오게 된다. 일반적으로 파일의 내용을 찾다가 중간에 멈춰야 할 필요가 있는 대용량 데이터는 위와 같은 코드를 많이 사용한다.

파일에 저장된 글자의 통계 정보 출력하기

  • 때로는 파일에 저장된 텍스트의 통계 정보를 읽어와야 할 때가 있다. 이를 위해 많이 사용하는 방법은 이미 배운 split() 함수와 len() 함수를 함께 사용하는 것이다.
  • 아래 코드는 파일에 저장된 모든 텍스트를 읽어온 후 차례대로 글자 수, 단어 수, 줄 수를 출력하는 프로그램이다.
with open("dream.txt", "r") as my_file:
    contents = my_file.read()
    word_list = contents.split("")   #빈칸 기준으로 단어를 분리하여 리스트
    line_list = contents.split("\\n") #한 줄씩 분리하여 리스트

print("총글자의수:",len(contents))
print("총 단어의 수:", len(word_list))
print("총 줄의 수:,', len(line_list))
총 글자의 수 188   #6행 실행결과
총 단어의 수: 35   #7행 실행결과
총 줄의 수: 7      #8행 실행결과

 

파일 쓰기

  • 파일에 무엇인가를 쓰기 위해서는 파일 열기 모드를 ‘W’로 설정하는 것과 함께 인코딩이라는 개념을 알아야 한다.
  • 텍스트 파일을 저장하기 위해서는 저장할 때 사용하는 표준을 지 정해야 하는데, 이것을 인코딩이라고 한다.
  • 인코딩 옵션으로 인코딩 표준을 지정할 수 있다. 일반적으로 인코딩은 utf 8을 많이 사용하며, 파일을 윈도에서만 사용한다면 cp949도 사용할 수 있다. 인코딩은 운영체제나 파일의 사용 환경에 따라 다르게 설정해야 한다.
  • 아래 코드를 실행하면 ‘count_log.txt’라는 파일을 ‘w’ 옵션으로 읽어온 후 1부터 10까지 의 숫자를 넣어 ‘1번째 줄이다.’부터 ‘10번째 줄이다.’까지 문자열을 만든 다음 f .write(data)코드로 count_log.txt 파일에 차례대로 기록한다. 모든 내용을 기록한 후에 f.close() 코드로 해당 파일을 닫는다.
f = open("count_log.txt", 'w', encoding = "utf8")
for i in range(l,ll):
    data ="%d번째 줄이다.\\n"%i
    f.write(data)
f.close()
  • 기존의 count_ log.txt 파일이 있음에도 다시 한번 w로 파일을 부르면 기존 파일이 삭제되고 새로운 파일이 생겨 새로운 내용만 기록된다. 상황에 따라 기존 파일에 계속 추가해야 하는 작업이 있을 수도 있다. 이 경우 많이 사용하는 방법은 추가 모드 a를 사용하는 것이다.

파일 열기 모드 a로 새로운 글 추가하기

  • 쓰기 모드인 w는 늘 새로운 파일을 생성한다.
with open("count_log.txt", 'a', encoding = "utf8") as f:
    for i in range(1, 11):
        data = "%d번째 줄이다.\\n"%i
        f.write(data)
  • 기존에 countjog.txt 파일이 있다는 가정에서 ‘a’ 모드로 파일을 연다. 코드를 수행할 때 마다 해당 파일에 ‘1번째 줄이다.’부터 ‘10번째 줄이다.’라는 텍스트가 계속 추가된다.

디렉터리 만들기

  • 파이썬으로는 파일만 다루는 것이 아니라 디렉터리도 함께 다룰 수 있다.
  • os 모듈을 사용하면 디렉터리를 쉽게 만들 수 있으며 디렉터 리를 생성하는 코드는 log이다.
import os
os.mkdir("log")

폴더 아래에 log라는 새로운 폴더가 생성

 

  • 하지만 프로그램 대부분이 새로 실행되므로 기존에 해당 디렉터리가 있는지 확인하는 코드가 필요하다. 이 경우 os.path.isdir 코드를 사용하여 기존 디렉터리의 존재 여부를 확인하면 된다.
import os
os.mkdir("log")

if not os.path.isdir("log"):
    os.mkdir("log")
Traceback (most recent call last):
    File "mkdir2.py", line 2, in <modulo>
        os.mkdir("log")
FileExistsError: [WinError 183] 파일이 이미 있으므로 만들 수 없습니다: 'log'

로그 파일 만들기

  • 이번에는 실제 프로그램에서 사용할 수 있는 로그 파일 log file
  • 로그 파일은 프로그램이 동작하는 동안 여러 가지 중간 기록을 저장하는 역할을 하는 파일이다. 예를 들어 웹 프로그램의 경우 외부 접속자의 접속 기록이나 접속 시간 등을 저장하는 파일이 로그 파일이다.
#로그 파일을 생성하는 코드

import os 

if not os.path.isdir("log"):
    os.mkdir("log")

if not os .path .exists("log/count_log .txt"):
    f = open("log/count_log.txt", 'w', encoding = "utf8")
    f.write("기록이 시작된다.\\n")
    f.close()

with open("log/count_log.txt", 'a', encoding = "utf8") as f:
    import random, datetime
    for i in range(1, 11):
        stamp = str(datetime .datetime .now())
        value = random.random()* 1000000
        logJLine = stamp + "\\t" + str(value) + "값이 생성되었다."+"\\n"
        f.write(log_line)
  • 3〜4행에서는 log 디렉터리가 존재하지 않을 경우 새롭게 디렉터리를 만든다.
  • 6〜9행에서는 기존에 한 번도 로그 기록이 없었다면 ‘w’ 모드로 count_log.txt 파일을 생성하고 기록의 시작을 알리는 문구를 저장한다.
  • 11〜17행은 예시를 만들기 위해 임의로 시간 기록과 함께 임의의 숫자를 문구 안에 계속 기록하여 저장한다.
  • 위 코드를 실행하면 딱 10번의 기록을 시행하지만 실제로는 해당 코드가 호출할 때마다 시간과 함께 임의의 숫자가 계속 기록된다.

 

pickle 모듈

  • 파이썬 프로그램을 실행할 때 생성되는 여러 변수와 객체는 순간적으로 메모리에 로딩되었다가 프로그램이 종료되면 사라진다. 하지만 때로는 이렇게 사용되는 객체를 저장시켜 놓고 필요할 때 다시 불러야 하는 경우가 있다.
  • 이 경우를 프로그래밍 언어에서는 영속화라고 한다.
  • 간단히 표현하면 필요한 객체를 파일로 저장시켜 다시 사용할 수 있도록 하는 것이다. 파이썬은 pickle 모듈을 제공하여 메모리에 로딩된 객체를 영속화 할 수 있도록 지원한다. 리스트에 들어간 데이터나 클래스의 오브젝트 등을 파일로 저장시켜 나중에 다시 사용할 수 있도록 해준다.
  • pickle 모듈을 사용하기 위해서는 다음 코드와 같이 호출한 후 객체를 저장할 수 있는 파일을 열고 저장하고자 하는 객체를 넘기면 된다.
  • 파일을 생성할 때는 ‘w’가 아닌 ‘wb’로 열어야 하는데. 여기서 B는 바이너리를 뜻하는 약자로 텍스트 파일이 아닌 바이너리 파일로 저장된다는 의미이다. dump() 함수에서는 저장할 객체, 저장될 파일 객체를 차례대로 인수로 넣으면 해당 객체가 해당 파일에 저장된다.
>>> import pickle
>>>
>>> f = open("list.pickle", "wb") 
>>> test = [1, 2, 3, 4, 5]
>>> pickle.dump(test, f)
>>> f.close()
  • 저장된 pickle 파일을 불러오는 프로세스도 저장 프로세스와 같다.
  • 먼저 list.pickle 파일을 ‘rb’ 모드로 읽어온 후, 해당 파일 객체를 pickle 모듈을 사용하여 load() 함수로 불러오면 된다.
  • 사실 pickle 객체를 불러올 때 해당 객체가 어떤 객체인지에 대한 정보가 전혀 없어 상당히 혼란스러울 때도 있지만, 해당 코드를 개발한 개발자라면 코드의 인터페이스를 정확히 알고 있으므로 어려움 없이 사용할 수 있다. 다음 파이썬 셸 코드는 앞에서 리스트 객체를nlist.pickle 파일에 저장했기 때문에 해당 파일을 불러 사용할 때도 동일하게 리스트 객체가 반환된 것을 알 수 있다.
>>> f = open("list.pickle", "rb") 
>>> test_pickle = pickle.load(f) 
>>> print(test_pickle)
[1, 2, 3, 4, 5]
>>> f.close()
  • pickle 모듈은 단순히 생성된 객체를 저장하는 기능도 있지만 사용자가 직접 생성한 클래스 객체도 저장한다.
  • 다음 코드와 같이 곱셈을 처리하는 클래스를 생성한다고 가정하자. 이 코드의 클래스는 처음 객체를 생성할 때 초깃값을 생성하고, multiply() 함수를 부를 때마다 ‘초깃값 * number’의 값을 호출하는 클래스이다. 일종의 곱셈 클래스라고 생각하면 된다.
>>> class Mutltiply(object):
...     def__init__(self,multiplier):
...         self.multiplier =multiplier
...     def multiply(self, number):
...         return number*self.multiplier
>>> muliply =Mutltiply(5) 
>>> muliply.multiply(10) 
50
  • 프로그램을 작성하다 보면 매우 복잡한 연산도 따로 저장하여 사용할 때가 있다. 이러한 저장 모듈을 효율적으로 사용하기 위해 다음 코드처럼 pickle 모듈을 사용할 수 있다
>>> import pickle 
>>>
>>> f = open("multiply_object.pickle", "wb")
>>> pickle.dump(muliply, f)
>>> f.closeO
>>>
>>> f = open("multiply_object.pickle", "rb")
>>>multiply_pickle=pickle.load(f) 
>>> multiply_pickle.multiply(5)
25