File / Exception / Log Handling

💡
예외 처리와 파일 다루기에 대해서 알아본다.

예외처리란?

예외 처리는 프로그램을 개발하며 반드시 신경써야하는 부분이다. 사전에 인지할 수 있는 예외에 대해서는 명시적으로 정의하여 처리해야 한다. 예상이 불가능한 예외에 대해서는 해당 상황에 대한 대처방안을 마련해야 한다. 인터프리터에 의해 프로그램이 바로 중단되지 않도록 대처를 해야하며, 이를 Exception Handling이라 한다.

Python Exception Handling Example

Python에서는 if문을 통해 예상 가능한 예외를 처리할 수 있으나, try ~ except 문법을 사용하기를 권장한다. 예시는 아래와 같다.

# try ~ except ~ else ~ finally example
lst = [1, 2, 3]

for i in range(5) :
    try :
        lst[i]
    except IndexError :
        print("run except block")
        print(e)
    else :
        print("run else block")
        print("i :", lst[i])
    finally :
        print("run final block\n")

# output
'''
run else block
i : 1
run final block

run else block
i : 2
run final block

run else block
i : 3
run final block

run except block
list index out of range
run final block

run except block
list index out of range
run final block
'''

강제 예외 발생

raise와 assert를 통해 특정 조건에 부합하지 않는 경우 강제로 Exception을 발생시킬 수 있다.

# raise excample
value = input("정수를 입력하세요.")
if not value.isdigit() :
    raise ValueError("정수가 아닙니다.")

# output
'''
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: 정수가 아닙니다.
'''
# assert example
value = input("정수를 입력하세요.")
assert isinstance(value, int)

# output
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
'''

File Handling

먼저, File은 Text File과 Binary File로 구분된다.Binary File은 사람이 읽을 수 없는 형태이며, 특정 Application을 통해서만 read 할 수 있다. (ex. ExcelWord 등) Text File은 메모장 등을 통해 read 할 수 있는, 문자열 형식으로 저장된 기본적인 파일을 의미한다.(ex. .txt file.py file 등) 최종적으로 컴퓨터는 이 Text File 조차도 Binary 파일로 변환하여 실행한다. (ex. .pyc file 등)

Python File I/O

Python에서 File을 입출력하는 기본 문법은 open 함수이다. parameter의 첫 번째에는 경로를 포함한 파일명을 입력하고, 두 번째에는 'r' 혹은 'rb'로 값을 전달한다. 이렇게 열린 File은 메모리에 올라온 상태가 되므로, 사용이 종료되면 close로 닫아주어야 메모리 누수가 생기지 않는다. 이는 with block을 통해 쉽게 사용할 수 있다. 블록이 끝나는 순간 File object는 파괴된다. open한 파일 객체은 File 객체의 readlines()readline()read()와 같은 메서드를 통해 내부의 정보를 활용할 수 있다. read()는 모든 문자열을 str type으로 반환하며, readlines()는 list type으로 반환한다. readline()은 한 줄씩 str type으로 반환한다.

 # read() example
 f = open("file_name.txt", "r")
 contents = f.read()
 print(type(contents))  # <class 'str'>
 f.close

 # with and readlines() example
 with open("file_name.txt", "r") as f :
     contents = f.readlines()  # <class 'list'>
     print(type(contents))

 # readline() example
 with open("file_name.txt", "r") as f :
     while True :
         line = f.readline()  # <class 'str'>
         for word in line.split() :
             print(word)  # word가 출력됨.
         if not line :
             break

File을 쓰기 위해서는 'w''wb''a''ab'모드로 open한다. w는 File의 기존 값을 없애고 덮어씌우는 개념이며, a는 File의 뒤에 이어쓰는 개념이다. open에는 encoding parameter가 있으며, 'utf8'과 'CP949'와 같은 값을 전달한다. utf8은 다국어 문자를 지원하는 체계로 가장 보변적으로 사용되고 있으며, CP949는 windows의 기본 encoding 체계이다.

 # 'w' mode example
 f = open("new_file_name.txt", "w", encoding="utf8")
 for i in range(1,11) :
     data = f.'{i}번째 줄입니다.\n'
     f.write(data)
 f.close

 # 'a' mode example
 with open("new_file_name.txt", "a", encoding="utf8") as f :
     for i in range(11, 20) :
         data = f.'{i}번째 줄입니다.\n'
         f.write(data)

Python Directory Handling

os 모듈과 pathlib 모듈을 통해 Directory를 관리할 수 있다. os 모듈은 path와 같은 Field의 값을 str로 반환하나, pathlib은 path 객체를 반환하여 더 다양하게 사용할 수 있다.

 # os module example
 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
         log_line = stamp + "\t" + str(value) + "값이 생성되었습니다"
         f.write(log_line)

 # pathlib module example
 import pathlib
 cwd = pathlib.Path.cwd()
 print(cwd.parent)  # 부모의 디렉토리 정보가 출력됨.
 print(cwd.parents)  # 모든 부모의 디렉토리 정보가 출력됨.
 print(cwd.glob)  # 디렉토리 내의 모든 파일 정보가 출력됨.

Pickle

Python이 실행되는 동안의 객체는 메모리에 올라와 있는 상태이며, 프로그램이 종료하면 동시에 메모리에서 삭제된다. 이를 영속화 시키기 위해 사용하는 것이 pickle이다. .pickle로 저장되는 File은 Binary File이다.

# pickle module example
import pickle

f = open("list.pickle", "wb")
test = [1,2,3,4,5]
pickle.dump(test, f)  # test 객체가 list.pickle file에 저장됨.
f.close()

f = open("list.pickle", "rb")
another = pickle.load(f)  # list.pickle에 있던 객체가 another에 할당됨.
f.close()

Log Handling

프로그램이 실행되는 동안 일어나는 정보를 기록하는 것을 의미한다. 해당 기록을 분석하기 위해서는 File로 저장해야 한다. 이를 위해 Python은 logging 모듈을 제공한다. logging의 메서드는 기본적으로 Console에만 출력을 해주며, default 설정으로는 warning부터 메세지가 출력된다. basicConfig()나 setLevel()등의 메서드를 통해 설정을 변경할 수 있다. FileHandler()와 addHandler()메서드를 사용해 Log를 File에 기록할 수 있다.

# logging module example
import logging

logging.debug('level 1')
logging.info('level 2')
logging.warning('level 3')  # default 설정으로 여기서부터 출력됨.
logging.error('level 4')
logging.critical('level 5')

# basicConfig() example
logging.basicConfig(level = logging.DEBUG)
logging.debug('now print this')

# setLevel() example
logger = logging.getLogger("main")
logger.setLevel(logging.INFO)
logging.debug('now do not print this')  # 설정에 의해 출력되지 않음.

# file write example
file = logging.FileHandler('thisIsFile.log', mode='w', enscoding='utf8')
logger.addHandler(file)  # log가 file에 저장됨.

Program Setting

실제 프로그램을 운영하기 위해선 여러 설정이 필요하다. 데이터 파일의 위치나, 파일 저장 장소 등의 정보를 설정해줄 방법이 필요한 것이다. 이를 위해 configparser와 argparser가 있다. configparser는 파일에 해당 정보를 저장하는 것이고, argparser는 실행 시점에 해당 정보를 전달하는 방식이다. 이에 대한 내용은 개인학습에서 다루겠다. 이렇게 설정된 파일은 Log의 설정으로 쓰일 수 있으며, Formatting을 통해 출력 혹은 저장될 수 있다.

# log config file example
import logging

logging.config.fileConfig('logging.conf')
logger = logging.getLogger()

# logging formater example
formatter = logging.Formatter(f'{time} {levelname} {provess} {message})


Python data handling

💡
CSV, 웹, XML, JSON 네 가지 데이터 타입에 대해 알아본다.

CSV

필드를 Comma로 구분한 Text File이다. 엑셀과 비슷한 양식의 데이터이며, 프로그램에 상관없이 쓸 수 있는 Text 형태의 File이다. open 함수를 통해 해당 File을 읽을수도 있으며, csv module을 통해 이를 객체 형태로 사용할수도 있다.

# open example
with open('file_name.csv') as f :
    while True :
        line = f.readline()
        if not line :
            break
        print(line.split(','))

# csv example
import csv

with open('file_name.csv') as f :
    csv_data = csv.reader(f)
    for row in csv_data :
        print(row)

HTML과 XML

Web에서 정보를 구조적으로 표현하기 위한 언어이다. Tag를 통해 요소를 구분하며, 트리형태의 포함관계를 가진다. Web에 표현되는 정보는 Browser가 HTML을 해석하여 표시한 결과이다. XML도 HTML과 비슷하며, 다른 기종 간 정보를 주고받기에 유용한 저장방식이다. 마찬가지로 Tag로 구분되며, 구조적인 정보를 표현한다. 두 데이터 형식 모두 Text로 구성된 Mark Up Language이다. 즉, Web의 모든 정보는 Text로 구성되어 있다는 의미이며, 이 구조를 이해하면 모든 정보를 활용할 수 있다.

Regular Expression

정규표현식이란, 복잡한 문자열 패턴을 정의하는 문자표현 공식을 의미한다. 이를 통해 특정한 규칙을 가진 문자열의 집합을 추출할 수 있다. HTML도 Tag를 통한 일정한 형식이 존재하기 때문에, 이를 활용하면 Web의 모든 정보를 활용할 수 있다. 정규식은 규칙이 매우 방대하기 때문에, 활용 시점마다 찾아보고 공부하는 것을 추천한다. 이때 유용한 도구는 '정규식 연습장'이다. (Link) Python은 re module을 제공하며 이를 통해 정규표현식을 사용할 수 있다. Web으로부터의 정보는 urllib 모듈의 request.urlopen()을 통해 불러올 수 있다.

# urllib and re example
import urllib.request
import re

url = 'https://www.naver.com/'
html = urllib.reqeust.urlopen(url)
html_contents = str(html.read().decode('utf8'))
lotto_num = re.findall("\<dl class=\'blind\'\>([/s/S]+?)(\<dl\>)", html_contents)
print(lotto_num)

BeautifulSoup

정규표현식을 통해 문자열을 parsing 하는 방법도 있으나, beautifulsoup 모듈을 통해 parsing을 할 수 있다. 속도는 상대적으로 느리나, 간편하다는 장점이 있다. 이는 외부 모듈이며, 내부적으로 lxml이나 html5lib과 같은 다른 라이브러리에 의존적이다.

# beautifulsoup example
from bs4 import BeautifulSoup

with open("books.xml", "r", encoding="utf8") as books_file:
    books_xml = books_file.read()

soup = BeautifulSoup(books_xml, "lxml")

for book_info in soup.find_all("author"):  # tag 가 'author'인 모든 정보
    print(book_info)
    print(book_info.get_text())  # 태그와 태그 사이의 값

JSON

JavaScript Object Notation의 줄임말로, 원래는 Javascript의 데이터 객체 표현 방식이다. 많은 장점으로 인해 대표적인 데이터 형태로 자리잡았다. 현재는 웹에서 제공하는 대부분의 API가 JSON을 활용하고 있다.JSON은 key : value 쌍으로 구성 되어 있어, Dict type의 객체와 유사하게 접근하여 사용할 수 있다. json 모듈이 있으며, json 객체도 Dict type이다.

# json file read example
import json

with open('json_file.json', 'r', encoding='utf8') as f :
    contents = f.read()
    json_data = json.loads(contents)  # dict type object
    print(json_data.items())

# json file write example
import json

dict_data = {'Name:Jack', 'Age':29, 'Sex':'Male'}
with open('new_json.json', 'w') as f :
    json.dump(dict_data, f)


과제

  • Baseball
  • Morsecode

isdigit() and isdecimal()

isdigit()은 3^2와 같은 모양의 문자열도 True를 반환한다. 만약 0~9까지의 숫자를 원하는 logic이라면, isdecimal()을 활용하는 것이 좋을 것 같다.

다중 조건 check기법

다양한 방법이 제시되었다. 예시는 아래와 같다.

# all
all(condition1(), condition2(), condition3())

# &=
result = True
result &= condition1()
result &= condition2()
result &= condition3()

# sum
sum((condition1(), condition2(), condition3())) == 3

nonlocal keyword

global과 local 중간의 느낌으로, 감싸고 있는 가까운 함수의 scope에 접근한다.

# nonlocal example
def outter(outter_var) :
    def inner() :
        nonlocal outter_var
        outter_var += 1
        return
    return

is_yes()

input_str.lower() in ['y', 'yes']과 같은 형태로 표현할 수 있다.

unittest

pycharm에서 unittest Module을 활용해 test를 수행할 수 있다.

Morsecode 판별

len(set(user_input)-{-. }) > 0와 같이 차집합 연산으로 확인하는 방법이 있었다.

Nested List Comprehension

중첩 List Comprehension으로 word 단위끼리 조합하고, 이후에 문장을 만드는 방법이 있었다.

정규표현식 사용

re.findall('[A-Za-z.!?]', input_str)으로 확인하고, 불필요한 문장부호는 re.sub('[.,!?]', '', input_str)로 지우는 방법이 있었다.


개인 학습

  • Logging
  • ConfigParser
  • ArgParser

Logging Module

Python에서 제공하는 logging Module에 대해 알아보았다. 다음 링크에서 많은 도움을 받았다. (Link)

logging work flow

logging의 work flow는 다음과 같다.

  1. Logger : 어플리케이션 코드가 직접 사용할 수 있는 인터페이스
  1. Handler : logger에 의해 만들어진 log를 적합한 위치로 전달
  1. Filter : 어떤 log가 출력되어야 하는지 결정
  1. Formatter : log 기록의 최종 출력본 레이아웃을 결정

먼저, Logger Class의 Instance를 생성한다. 이때 logger는 name을 가지며, logger는 다른 인스턴스와 계층적 관계를 가진다. 이후, Handler를 통해 log를 어디에 기록할지 설정한다. Filter는 level보다 복잡한 필터링을 원할 때 사용할 수 있다. 마지막으로, Formatter는 출력되는 형식을 결정한다.

# logger example
import logging

logger = logging.getLogger('name')  # name을 설정하지 않는 경우 'root'
logger.setLevel(logging.INFO)
logger.info('Message')

# handler example
stream_handler = logging.StreamHandler()
file_handler = logging.FileHandler(filename = 'infomation.log')

# formatter example
formatter = logging.Formatter(fmt=f"{asctime} - {name} - {levelname} - {message}")

# handler setting
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)  # console에는 info부터 출력
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(fotmatter)  # file에는 debug부터 출력

Config Parser란?

Python에서 제공하는 configparser module은 설정 정보를 file형태로 손쉽게 입출력할 수 있게 한다. 기본적으로 Dict type의 형태로 구성되어 있으며, I/O가 손쉽다. 다음 링크에서 많은 도움을 받았다. (Link)

configparser 예시

configparser를 통해 file을 생성하고, 읽어오는 예시이다.

# create config file example
from configparser import ConfigParser

config = ConfigParser()  # Dict type object

config['settings'] = {
    'debug': 'true',
    'secret_key': 'abc123',
    'log_path': '/my_app/log'
}

config['db'] = {
    'db_name': 'myapp_dev',
    'db_host': 'localhost',
    'db_port': '8889'
}

config['files'] = {
    'use_cdn': 'false',
    'images_path': '/my_app/images'
}
with open('./dev.ini', 'w') as f:
    config.write(f)  # 'dev.ini' file creadted

# read config file example
from configparser import ConfigParser

parser = ConfigParser()
parser.read('dev.ini')
print(parser.sections())  # ['settings', 'db', 'files']
print(parser.options('settings'))  # ['debug', 'secret_key', 'log_path']
print(parser.get('settings', 'secret_key'))  # abc123

ArgParser란?

Python에서는 CLI 환경에서 주어지는 input variable을 처리할 수 있도록 argparser Module을 제공한다. 해당 모듈을 통해 프로그램이 실행될 때 여러 설정을 조절할 수 있다.

argparser 예시

argparser Module의 사용 예시이다.

# argparser example
import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('X', type=int,
                help="What is the first number?")
    parser.add_argument('Y', type=int,
                help="What is the second number?")

    args = parser.parse_args()
    X = args.X
    Y = args.Y
    print("%d + %d = %d" % (X, Y, X+Y))

if __name__=="__main__":
    main()

+ Recent posts