사용자의 모든 반응을 예상하여 프로그램을 만드는 것은 생각보다 쉬운일이 아니다. 그렇지만 예상치 못한 반응으로 프로그램이 비정상적으로 작동되는것을 막기위해서 예외(Exception) 핸들링 이 필요하다.
예상 가능한 예외
예상 불가능한 예외
파이썬에서는 예외처리를 위해 try~except~finally
,try~except~else
구문을 제공해준다.
for i in range(10):
try:
result=10/i
except ZeroDivisionError as e:
print('Not divided by 0')
else:
print('예외가 발생하지 않았을 때 동작합니다')
finally:
print('예외 발생 여부와 상관없이 무조건 실행됩니다.')
print('--------------------------------------------------')
만약 사용자의 반응이 프로그램에 큰 악영향을 끼칠수 있을경우 프로그램의 작동을 멈추는 것이 최선의 선택일수도 있다. raise
와 assert
를 통해 예외를 발생시킬 수 있다.
user_input=input('음이 아닌 정수를 입력해주세요 : ')
for digit in user_input:
if digit not in '0123456789':
raise ValueError('음이 아닌 정수를 입력해주세요)
def append_int(src,data):
assert isinstance(src,list) # list가 아니면 예외
assert isinstance(data,int) # int가 아니면 예외
src.append(data)
기본적으로 파일은 text 파일 과 binary 파일 로 나뉜다. 물론 모든 text 파일도 실제로는 binary 파일이지만 ASCII/UNICODE 등 인코딩 형식에 따라 문자열집합으로 변환되어 사람이 읽을수 있다.
Binary 파일 | Text 파일 |
---|---|
컴퓨터만 이해할 수 있는 형태인 이진법 형식으로 저장된 파일 | 인간도 이해할 수 있는 문자열 형식으로 저장된 파일 |
일반적으로 메모장으로 열면 내용이 깨져보인다 | 메모장으로 열면 확인 가능 |
엑셀파일,워드파일 등 | 메모장에 저장된 파일, HTML 파일, 파이썬 코드파일 등 |
파이썬에서는 기본적으로 다음과 같이 파일핸들링이 이루어진다.
파일모드 | 설명 |
---|---|
r | 읽기모드 - 파일을 읽기만 할 때 사용 (디폴트) |
w | 쓰기모드 - 파일에 내용을 쓸 때 사용(내용이 있을경우 지우고 새로쓴다) |
a | 추가모드 - 파일의 마지막에 새로운 내용을 추가 시킬때 사용 |
t | 텍스트파일 - 텍스트 문자 기록에 사용 (디폴트) |
b | 바이너리파일 - 바이트 단위 데이터 기록에 사용 |
f=open('boostcamp.txt','r')
contents=f.read() # 모든 데이터를 문자열로 읽어옵니다.
print(contents)
f.close() # 파일을 닫아줘야합니다.
#################################
# 파일을 닫는게 귀찮다면 with를 이용합시다.
with open('boostcamp.txt','r') as f:
line=f.readline() # 데이터를 한줄씩 읽어온다.
print(line)
#################################
with open('boostcamp.txt','r') as f:
lines=f.readlines() # 모든 데이터를 list로 반환한다.
print(lines)
#################################
# encoding 규칙은 상황에 맞게 정합니다.
with open('boostcamp.txt','a',encoding='utf-8') as f:
for i in range(1,6):
f.write(f'{i}번째 학습정리입니다.') # 데이터를 파일에 저장합니다.
일반적으로 텍스트를 파일로 저장할 때는 파일 입출력을 이용하지만, 리스트나 클래스 같은 객체의 경우 데이터를 저장하거나 불러올 수 없다. 그래서 파이썬에서는 이와 같은 텍스트 이외의 데이터를 파일로 저장하기 위해 pickle
모듈을 제공한다.
import pickle
class Multiply:
def __init__(self,multiplier):
self.multiplier=multiplier
def multiply(self,number):
return number*self.multiplier
calculator=Multiply(5)
print(calculator.multiply(10)) #50
# 객체 저장
with open('multiply_object.pickle','wb') as f:
pickle.dump(calculator,f)
del calculator # 객체 삭제
# 객체 로드
with open('multiply_object.pickle','rb') as f:
calculator_pickle=pickle.load(f)
print(calculator_pickle.multiply(5)) # 25
<참고>
위의 예제들을 살펴보면 파일 핸들링을 위해서는 file path를 다뤄야 한다. 그런데 운영체제에 따라 path 표현방식이 달라 문제가 생기는 경우가 있다. 그래서 최근에는 pathlib 모듈을 사용하여 path 자체를 객체로 사용하여 다루기도 한다.
프로그램을 개발하면 개발자가 의도한대로 작동하지 않는 경우가 상당히 자주 나온다. 그래서 로그(Log) 는 시스템 발생 장애 및 이상 징후를 모니터링 할 수 있는 좋은 판단 기준이다. 이를 위해 파이썬에서는 logging
모듈을 제공한다.
Level | 개요 | 예시 |
---|---|---|
debug | 개발시 처리 기록을 남겨야하는 로그정보를 남김 | - 다음함수로 A를 호출함 - 변수 A를 무엇으로 변경함 |
info | 처리가 진행되는 동안의 정보를 알림 | - 서버가 시작되었음 - 서버가 종료됨 - 사용자 A가 프로그램에 접속함 |
warning | 사용자가 잘못 입력한 정보나 처리는 가능하나, 원래 개발시 의도치 않는 정보가 들어왔을때 알림 | - str 입력을 기대했으나, int가 입력됨 -> str casting 으로 처리함 - 함수에 argument로 이차원 리스트를 기대했으나 일차원리스트가 들어옴 -> 이차원으로 변환후 처리 |
error | 잘못된 처리로 인해 에러가 났으나, 프로그램은 동작할수있음을 알림 | - 파일에 기록을 해야하는데 파일이 없음 -> Exception 처리 후 사용자에게 알림 - 외부서비스와 연결 불가 |
critical | 잘못된 처리로 데이터 손실이나 더이상 프로그램이 동작할 수 없음을 알림 | - 잘못된 접근으로 해당 파일이 삭제됨 - 사용자의 의한 강제종료. |
import logging
#Stream(콘솔)에 로그 남기기
logging.debug('debug log')
logging.info('info log')
logging.warning('warning log')
logging.error('error log')
logging.critical('critical log')
# WARNING:root:warning log
# ERROR:root:error log
# CRITICAL:root:critical log
debug,info level 이 나오지 않는 이유 는 기본적으로 logging
의 default level 이 warning 으로 되어 있기 때문이다. 만약 level을 변경하고 싶다면 다음과 같이 설정한다.
import logging
logging.basicConfig(level=logging.DEBUG) # DEBUG level 설정
logging.debug('info log')
logging.info('info log')
logging.warning('info log')
logging.error('info log')
logging.critical('info log')
# DEBUG:root:debug log
# INFO:root:info log
# WARNING:root:warning log
# ERROR:root:error log
# CRITICAL:root:critical log
만약 로그 형식 변경 과 로그를 파일로 기록 하고 싶다면 다음과 같이 설정한다.
import logging
#logger instance 생성
logger=logging.getLogger()
#logger formatter 생성
#logRecode attributes
formatter=logging.Formatter('[%(asctime)s][%(levelname)s]|[%(filename)s:%(lineno)s]>> %(message)s')
#logger level 셋팅
logger.setLevel(logging.DEBUG)
# file & stream handler 생성
file_handler=logging.FileHandler('logging_example.log') # 로그 파일 핸들러
stream_handler=logging.StreamHandler() # 로그 스트림(콘솔) 핸들러
# handler 에 formatter 설정
file_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# logger instance에 handler 등록, 스트림과 파일에 동시 로그 출력
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
logger.debug('debug_test')
logger.info('info_test')
logger.warning('warning_test')
logger.error('error_test')
logger.critical('critical_test')
# [2021-01-23 00:30:53,444][DEBUG]|[logging_example.py:45]>> debug_test
# [2021-01-23 00:30:53,445][INFO]|[logging_example.py:46]>> info_test
# [2021-01-23 00:30:53,447][WARNING]|[logging_example.py:47]>> warning_test
# [2021-01-23 00:30:53,449][ERROR]|[logging_example.py:48]>> error_test
# [2021-01-23 00:30:53,450][CRITICAL]|[logging_example.py:49]>> critical_test
프로그램을 만들다보면, 상황에 따라 설정파일의 변동이 있다. 예를들어, 파일들의 주소, 출력파일을 저장할 디렉토리 주소 등이다. 이와 같이 프로그램을 실행시킬때마다 설정을 다르게 해주고 싶을 때 유용한 configparser
모듈이 있다.
#config.cfg
[SectionOne]
Status:Single
Age:20
Single:True
[SectionTwo]
FavortieColor=Green
import configparser
config=configparser.ConfigParser()
config.read('config.cfg') # Config 파일 읽어옵니다.
print(config.sections()) # Config 파일의 섹션 정보를 가져옵니다, ['SectionOne','SectionTwo']
for key in config['SectionOne']: # dict 자료형처럼 사용가능합니다.
print(key) # status,age,single
print(config['SectionOne']['status']) # 'SectionOne' 에 있는 'status' 값을 가져옵니다. # Single
파이썬 프로그램이나 커맨드라인에서 인자 옵션을 주었던 경험이 있을 것이다. 이와 같이 console 창에서 프로그램 실행시 setting 정보를 저장할수 있도록 도와주는 argparse
모듈이 있다.
#parser_example.py
import argparse
parser=argparse.ArgumentParser(description='Argparser example')
parser.add_argument('-a','--a_value',dest='A_value',help='A integers',type=int)
parser.add_argument('-b','--b_value',dest='B_value',help='B integers',type=int)
args=parser.parse_args()
print(args)
print(args.A_value)
print(args.B_value)
>> parser_example.py -a 4 --b_value 5
Namespace(A_value=4,B_value=5)
4
5